Merge "Fix storaged memory leak"
diff --git a/.clang-format-2 b/.clang-format-2
deleted file mode 100644
index ede5d7e..0000000
--- a/.clang-format-2
+++ /dev/null
@@ -1,9 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 2
-PointerAlignment: Left
-TabWidth: 2
-UseTab: Never
diff --git a/.clang-format-2 b/.clang-format-2
new file mode 120000
index 0000000..7ab20d4
--- /dev/null
+++ b/.clang-format-2
@@ -0,0 +1 @@
+../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/.clang-format-4 b/.clang-format-4
deleted file mode 100644
index 55773a2..0000000
--- a/.clang-format-4
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-ContinuationIndentWidth: 8
-PointerAlignment: Left
-TabWidth: 4
-UseTab: Never
diff --git a/.clang-format-4 b/.clang-format-4
new file mode 120000
index 0000000..ddcf5a2
--- /dev/null
+++ b/.clang-format-4
@@ -0,0 +1 @@
+../../build/soong/scripts/system-clang-format
\ No newline at end of file
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 0e43dae..f6ef906 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -78,3 +78,13 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services.img)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c47230f..66d0c92 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,6 +2,46 @@
"presubmit": [
{
"name": "adbd_test"
+ },
+ {
+ "name": "debuggerd_test"
+ },
+ {
+ "name": "fs_mgr_unit_test"
+ },
+ {
+ "name": "fs_mgr_vendor_overlay_test"
+ },
+ {
+ "name": "init_tests"
+ },
+ {
+ "name": "libbase_test"
+ },
+ {
+ "name": "libprocinfo_test"
+ },
+ {
+ "name": "libutils_test"
+ },
+ {
+ "name": "memunreachable_test"
+ },
+ {
+ "name": "memunreachable_unit_test"
+ },
+ {
+ "name": "memunreachable_unit_test",
+ "host": true
+ },
+ {
+ "name": "memunreachable_binder_test"
+ },
+ {
+ "name": "propertyinfoserializer_tests"
+ },
+ {
+ "name": "ziparchive-tests"
}
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 00e98fe..06cfcbf 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -22,36 +22,19 @@
"-Wexit-time-destructors",
"-Wno-unused-parameter",
"-Wno-missing-field-initializers",
+ "-Wthread-safety",
"-Wvla",
+ "-DADB_HOST=1", // overridden by adbd_defaults
+ "-DALLOW_ADBD_ROOT=0", // overridden by adbd_defaults
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
+ "-DENABLE_FASTDEPLOY=1", // enable fast deploy
],
- rtti: true,
- cpp_std: "gnu++17",
+ cpp_std: "experimental",
use_version_lib: true,
-
compile_multilib: "first",
- product_variables: {
- debuggable: {
- cflags: [
- "-DALLOW_ADBD_ROOT",
- "-DALLOW_ADBD_DISABLE_VERITY",
- "-DALLOW_ADBD_NO_AUTH",
- ],
- },
- },
target: {
- android: {
- cflags: [
- "-DADB_HOST=0",
- "-Wthread-safety",
- ],
- },
-
- host: {
- cflags: ["-DADB_HOST=1"],
- },
-
darwin: {
host_ldlibs: [
"-lpthread",
@@ -72,11 +55,17 @@
"-DUNICODE=1",
"-D_UNICODE=1",
- // -std=gnu++11 doesn't set _GNU_SOURCE on Windows.
+ // Unlike on Linux, -std=gnu++ doesn't set _GNU_SOURCE on Windows.
"-D_GNU_SOURCE",
// MinGW hides some things behind _POSIX_SOURCE.
"-D_POSIX_SOURCE",
+
+ // libusb uses __stdcall on a variadic function, which gets ignored.
+ "-Wno-ignored-attributes",
+
+ // Not supported yet.
+ "-Wno-thread-safety",
],
host_ldlibs: [
@@ -85,15 +74,47 @@
"-luserenv",
],
},
+ },
+}
- not_windows: {
+cc_defaults {
+ name: "adbd_defaults",
+ defaults: ["adb_defaults"],
+
+ cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+ product_variables: {
+ debuggable: {
cflags: [
- "-Wthread-safety",
+ "-UALLOW_ADBD_ROOT",
+ "-DALLOW_ADBD_ROOT=1",
+ "-DALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_NO_AUTH",
],
},
},
}
+cc_defaults {
+ name: "host_adbd_supported",
+
+ host_supported: true,
+ target: {
+ linux: {
+ enabled: true,
+ host_ldlibs: [
+ "-lresolv", // b64_pton
+ "-lutil", // forkpty
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
// libadb
// =========================================================
// These files are compiled for both the host and the device.
@@ -104,7 +125,8 @@
"adb_trace.cpp",
"adb_unique_fd.cpp",
"adb_utils.cpp",
- "fdevent.cpp",
+ "fdevent/fdevent.cpp",
+ "fdevent/fdevent_poll.cpp",
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
@@ -124,7 +146,7 @@
"adb_io_test.cpp",
"adb_listeners_test.cpp",
"adb_utils_test.cpp",
- "fdevent_test.cpp",
+ "fdevent/fdevent_test.cpp",
"socket_spec_test.cpp",
"socket_test.cpp",
"sysdeps_test.cpp",
@@ -142,8 +164,6 @@
"client/usb_libusb.cpp",
"client/usb_dispatch.cpp",
"client/transport_mdns.cpp",
- "client/fastdeploy.cpp",
- "client/fastdeploycallbacks.cpp",
],
generated_headers: ["platform_tools_version"],
@@ -178,9 +198,6 @@
"libdiagnose_usb",
"libmdnssd",
"libusb",
- "libandroidfw",
- "libziparchive",
- "libz",
"libutils",
"liblog",
"libcutils",
@@ -205,6 +222,7 @@
target: {
windows: {
enabled: true,
+ ldflags: ["-municode"],
shared_libs: ["AdbWinApi"],
},
},
@@ -253,25 +271,33 @@
"client/console.cpp",
"client/adb_install.cpp",
"client/line_printer.cpp",
+ "client/fastdeploy.cpp",
+ "client/fastdeploycallbacks.cpp",
"shell_service_protocol.cpp",
],
+ generated_headers: [
+ "bin2c_fastdeployagent",
+ "bin2c_fastdeployagentscript"
+ ],
+
static_libs: [
"libadb_host",
+ "libandroidfw",
"libbase",
"libcutils",
"libcrypto_utils",
"libcrypto",
+ "libfastdeploy_host",
"libdiagnose_usb",
"liblog",
"libmdnssd",
+ "libprotobuf-cpp-lite",
"libusb",
- "libandroidfw",
- "libziparchive",
- "libz",
"libutils",
"liblog",
- "libcutils",
+ "libziparchive",
+ "libz",
],
stl: "libc++_static",
@@ -281,9 +307,14 @@
// will violate ODR
shared_libs: [],
- required: [
- "deploypatchgenerator",
- ],
+ // Archive adb, adb.exe.
+ dist: {
+ targets: [
+ "dist_files",
+ "sdk",
+ "win_sdk",
+ ],
+ },
target: {
darwin: {
@@ -305,7 +336,7 @@
// libadbd_core contains the common sources to build libadbd and libadbd_services.
cc_library_static {
name: "libadbd_core",
- defaults: ["adb_defaults"],
+ defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
// libminadbd wants both, as it's used to build native tests.
@@ -314,9 +345,6 @@
srcs: libadb_srcs + libadb_posix_srcs + [
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
- "daemon/usb.cpp",
- "daemon/usb_ffs.cpp",
- "daemon/usb_legacy.cpp",
],
local_include_dirs: [
@@ -326,8 +354,8 @@
generated_headers: ["platform_tools_version"],
static_libs: [
+ "libadbconnection_server",
"libdiagnose_usb",
- "libqemu_pipe",
],
shared_libs: [
@@ -338,21 +366,36 @@
"libcutils",
"liblog",
],
+
+ target: {
+ android: {
+ whole_static_libs: [
+ "libqemu_pipe",
+ ],
+ srcs: [
+ "daemon/transport_qemu.cpp",
+ "daemon/usb.cpp",
+ "daemon/usb_ffs.cpp",
+ "daemon/usb_legacy.cpp",
+ ]
+ },
+ linux_glibc: {
+ srcs: [
+ "daemon/usb_dummy.cpp",
+ ]
+ }
+ },
}
cc_library {
name: "libadbd_services",
- defaults: ["adb_defaults"],
+ defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
compile_multilib: "both",
srcs: [
"daemon/file_sync_service.cpp",
- "daemon/framebuffer_service.cpp",
- "daemon/mdns.cpp",
- "daemon/remount_service.cpp",
"daemon/services.cpp",
- "daemon/set_verity_enable_state_service.cpp",
"daemon/shell_service.cpp",
"shell_service_protocol.cpp",
],
@@ -363,31 +406,61 @@
],
static_libs: [
+ "libadbconnection_server",
"libadbd_core",
- "libavb_user",
"libdiagnose_usb",
- "libqemu_pipe",
],
shared_libs: [
"libasyncio",
"libbase",
- "libbootloader_message",
"libcrypto",
"libcrypto_utils",
"libcutils",
- "libext4_utils",
- "libfec",
- "libfs_mgr",
"liblog",
- "libmdnssd",
- "libselinux",
],
+
+ product_variables: {
+ debuggable: {
+ required: [
+ "remount",
+ ],
+ },
+ },
+
+ target: {
+ android: {
+ srcs: [
+ "daemon/abb_service.cpp",
+ "daemon/framebuffer_service.cpp",
+ "daemon/mdns.cpp",
+ "daemon/reboot_service.cpp",
+ "daemon/remount_service.cpp",
+ "daemon/restart_service.cpp",
+ "daemon/set_verity_enable_state_service.cpp",
+ ],
+ static_libs: [
+ "libavb_user",
+ ],
+ shared_libs: [
+ "libbootloader_message",
+ "libmdnssd",
+ "libfec",
+ "libfs_mgr",
+ "libselinux",
+ ],
+ },
+ recovery: {
+ exclude_srcs: [
+ "daemon/abb_service.cpp",
+ ],
+ },
+ },
}
cc_library {
name: "libadbd",
- defaults: ["adb_defaults"],
+ defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
// Avoid getting duplicate symbol of android::build::GetBuildNumber().
@@ -418,7 +491,7 @@
cc_binary {
name: "adbd",
- defaults: ["adb_defaults"],
+ defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
srcs: [
@@ -447,9 +520,90 @@
],
}
+cc_binary {
+ name: "static_adbd",
+ defaults: ["adbd_defaults", "host_adbd_supported"],
+
+ recovery_available: false,
+ static_executable: true,
+ host_supported: false,
+
+ srcs: [
+ "daemon/main.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbconnection_server",
+ "libadbd",
+ "libadbd_services",
+ "libasyncio",
+ "libavb_user",
+ "libbase",
+ "libbootloader_message",
+ "libcap",
+ "libcrypto",
+ "libcrypto_utils",
+ "libcutils",
+ "libdiagnose_usb",
+ "libext4_utils",
+ "libfec",
+ "libfec_rs",
+ "libfs_mgr",
+ "liblog",
+ "liblp",
+ "libmdnssd",
+ "libminijail",
+ "libselinux",
+ "libsquashfs_utils",
+ ],
+}
+
+cc_binary {
+ name: "abb",
+
+ defaults: ["adbd_defaults"],
+ recovery_available: false,
+
+ srcs: [
+ "daemon/abb.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbd_core",
+ "libadbd_services",
+ "libcmd",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libutils",
+ "libselinux",
+ ],
+}
+
cc_test {
name: "adbd_test",
- defaults: ["adb_defaults"],
+ defaults: ["adbd_defaults"],
srcs: libadb_test_srcs + [
"daemon/services.cpp",
"daemon/shell_service.cpp",
@@ -512,3 +666,90 @@
},
},
}
+
+// Note: using pipe for xxd to control the variable name generated
+// the default name used by xxd is the path to the input file.
+java_genrule {
+ name: "bin2c_fastdeployagent",
+ out: ["deployagent.inc"],
+ srcs: [":deployagent"],
+ cmd: "(echo 'unsigned char kDeployAgent[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+genrule {
+ name: "bin2c_fastdeployagentscript",
+ out: ["deployagentscript.inc"],
+ srcs: ["fastdeploy/deployagent/deployagent.sh"],
+ cmd: "(echo 'unsigned char kDeployAgentScript[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+cc_library_host_static {
+ name: "libfastdeploy_host",
+ defaults: ["adb_defaults"],
+ srcs: [
+ "fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
+ "fastdeploy/deploypatchgenerator/patch_utils.cpp",
+ "fastdeploy/proto/ApkEntry.proto",
+ ],
+ static_libs: [
+ "libadb_host",
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "liblog",
+ "libmdnssd",
+ "libusb",
+ "libutils",
+ "libziparchive",
+ "libz",
+ ],
+ stl: "libc++_static",
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+ target: {
+ windows: {
+ enabled: true,
+ shared_libs: ["AdbWinApi"],
+ },
+ },
+}
+
+cc_test_host {
+ name: "fastdeploy_test",
+ defaults: ["adb_defaults"],
+ srcs: [
+ "fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
+ "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
+ ],
+ static_libs: [
+ "libadb_host",
+ "libandroidfw",
+ "libbase",
+ "libcutils",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "libfastdeploy_host",
+ "liblog",
+ "libmdnssd",
+ "libprotobuf-cpp-lite",
+ "libusb",
+ "libutils",
+ "libziparchive",
+ "libz",
+ ],
+ target: {
+ windows: {
+ enabled: true,
+ shared_libs: ["AdbWinApi"],
+ },
+ },
+ data: [
+ "fastdeploy/testdata/rotating_cube-release.apk",
+ ],
+}
diff --git a/adb/Android.mk b/adb/Android.mk
deleted file mode 100644
index 8b2d558..0000000
--- a/adb/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-# Archive adb, adb.exe.
-$(call dist-for-goals,dist_files sdk win_sdk,$(HOST_OUT_EXECUTABLES)/adb)
-
-ifdef HOST_CROSS_OS
-$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
-endif
diff --git a/adb/NOTICE b/adb/NOTICE
index ff47c95..9ffcc08 100644
--- a/adb/NOTICE
+++ b/adb/NOTICE
@@ -189,63 +189,3 @@
END OF TERMS AND CONDITIONS
-------------------------------------------------------------
-libwinpthread license:
-------------------------------------------------------------
-Copyright (c) 2011 mingw-w64 project
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-
-/*
- * Parts of this library are derived by:
- *
- * Posix Threads library for Microsoft Windows
- *
- * Use at own risk, there is no implied warranty to this code.
- * It uses undocumented features of Microsoft Windows that can change
- * at any time in the future.
- *
- * (C) 2010 Lockless Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * * Neither the name of Lockless Inc. nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AN
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9c0eeca..24d4292 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -280,6 +280,9 @@
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
t->SetConnectionState(kCsSideload);
+ } else if (type == "rescue") {
+ D("setting connection_state to kCsRescue");
+ t->SetConnectionState(kCsRescue);
} else {
D("setting connection_state to kCsHost");
t->SetConnectionState(kCsHost);
@@ -357,9 +360,14 @@
case A_OPEN: /* OPEN(local-id, 0, "destination") */
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
- // TODO: Switch to string_view.
- std::string address(p->payload.begin(), p->payload.end());
- asocket* s = create_local_service_socket(address.c_str(), t);
+ std::string_view address(p->payload.begin(), p->payload.size());
+
+ // Historically, we received service names as a char*, and stopped at the first NUL
+ // byte. The client sent strings with null termination, which post-string_view, start
+ // being interpreted as part of the string, unless we explicitly strip them.
+ address = StripTrailingNulls(address);
+
+ asocket* s = create_local_service_socket(address, t);
if (s == nullptr) {
send_close(0, p->msg.arg0, t);
} else {
@@ -600,7 +608,7 @@
fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
fprintf(stderr, "Server had pid: %d\n", pid);
- android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
+ android::base::unique_fd fd(unix_open(GetLogFilePath(), O_RDONLY));
if (fd == -1) return;
// Let's not show more than 128KiB of log...
@@ -1013,9 +1021,10 @@
return 0;
}
-bool handle_host_request(const char* service, TransportType type, const char* serial,
- TransportId transport_id, int reply_fd, asocket* s) {
- if (strcmp(service, "kill") == 0) {
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+ const char* serial, TransportId transport_id, int reply_fd,
+ asocket* s) {
+ if (service == "kill") {
fprintf(stderr, "adb server killed by remote request\n");
fflush(stdout);
@@ -1027,29 +1036,49 @@
exit(0);
}
- // "transport:" is used for switching transport with a specified serial number
- // "transport-usb:" is used for switching transport to the only USB transport
- // "transport-local:" is used for switching transport to the only local transport
- // "transport-any:" is used for switching transport to the only transport
- if (!strncmp(service, "transport", strlen("transport"))) {
+ LOG(DEBUG) << "handle_host_request(" << service << ")";
+
+ // Transport selection:
+ if (service.starts_with("transport") || service.starts_with("tport:")) {
TransportType type = kTransportAny;
- if (!strncmp(service, "transport-id:", strlen("transport-id:"))) {
- service += strlen("transport-id:");
- transport_id = strtoll(service, const_cast<char**>(&service), 10);
- if (*service != '\0') {
- SendFail(reply_fd, "invalid transport id");
- return true;
+ std::string serial_storage;
+ bool legacy = true;
+
+ // New transport selection protocol:
+ // This is essentially identical to the previous version, except it returns the selected
+ // transport id to the caller as well.
+ if (android::base::ConsumePrefix(&service, "tport:")) {
+ legacy = false;
+ if (android::base::ConsumePrefix(&service, "serial:")) {
+ serial_storage = service;
+ serial = serial_storage.c_str();
+ } else if (service == "usb") {
+ type = kTransportUsb;
+ } else if (service == "local") {
+ type = kTransportLocal;
+ } else if (service == "any") {
+ type = kTransportAny;
}
- } else if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
- type = kTransportUsb;
- } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
- type = kTransportLocal;
- } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
- type = kTransportAny;
- } else if (!strncmp(service, "transport:", strlen("transport:"))) {
- service += strlen("transport:");
- serial = service;
+
+ // Selection by id is unimplemented, since you obviously already know the transport id
+ // you're connecting to.
+ } else {
+ if (android::base::ConsumePrefix(&service, "transport-id:")) {
+ if (!ParseUint(&transport_id, service)) {
+ SendFail(reply_fd, "invalid transport id");
+ return HostRequestResult::Handled;
+ }
+ } else if (service == "transport-usb") {
+ type = kTransportUsb;
+ } else if (service == "transport-local") {
+ type = kTransportLocal;
+ } else if (service == "transport-any") {
+ type = kTransportAny;
+ } else if (android::base::ConsumePrefix(&service, "transport:")) {
+ serial_storage = service;
+ serial = serial_storage.c_str();
+ }
}
std::string error;
@@ -1058,27 +1087,29 @@
s->transport = t;
SendOkay(reply_fd);
- // We succesfully handled the device selection, but there's another request coming.
- return false;
+ if (!legacy) {
+ // Nothing we can do if this fails.
+ WriteFdExactly(reply_fd, &t->id, sizeof(t->id));
+ }
+
+ return HostRequestResult::SwitchedTransport;
} else {
SendFail(reply_fd, error);
- return true;
+ return HostRequestResult::Handled;
}
}
// return a list of all connected devices
- if (!strncmp(service, "devices", 7)) {
- bool long_listing = (strcmp(service+7, "-l") == 0);
- if (long_listing || service[7] == 0) {
- D("Getting device list...");
- std::string device_list = list_transports(long_listing);
- D("Sending device list...");
- SendOkay(reply_fd, device_list);
- }
- return true;
+ if (service == "devices" || service == "devices-l") {
+ bool long_listing = service == "devices-l";
+ D("Getting device list...");
+ std::string device_list = list_transports(long_listing);
+ D("Sending device list...");
+ SendOkay(reply_fd, device_list);
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "reconnect-offline")) {
+ if (service == "reconnect-offline") {
std::string response;
close_usb_devices([&response](const atransport* transport) {
if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
@@ -1086,15 +1117,15 @@
return true;
}
return false;
- });
+ }, true);
if (!response.empty()) {
response.resize(response.size() - 1);
}
SendOkay(reply_fd, response);
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "features")) {
+ if (service == "features") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t != nullptr) {
@@ -1102,10 +1133,10 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "host-features")) {
+ if (service == "host-features") {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
if (should_use_libusb()) {
@@ -1113,45 +1144,47 @@
}
features.insert(kFeaturePushSync);
SendOkay(reply_fd, FeatureSetToString(features));
- return true;
+ return HostRequestResult::Handled;
}
// remove TCP transport
- if (!strncmp(service, "disconnect:", 11)) {
- const std::string address(service + 11);
+ if (service.starts_with("disconnect:")) {
+ std::string address(service.substr(11));
if (address.empty()) {
kick_all_tcp_devices();
SendOkay(reply_fd, "disconnected everything");
- return true;
+ return HostRequestResult::Handled;
}
std::string serial;
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
std::string error;
- if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
+ if (address.starts_with("vsock:")) {
+ serial = address;
+ } else if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
address.c_str(), error.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
atransport* t = find_transport(serial.c_str());
if (t == nullptr) {
SendFail(reply_fd, android::base::StringPrintf("no such device '%s'", serial.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
kick_transport(t);
SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
- return true;
+ return HostRequestResult::Handled;
}
// Returns our value for ADB_SERVER_VERSION.
- if (!strcmp(service, "version")) {
+ if (service == "version") {
SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
- return true;
+ return HostRequestResult::Handled;
}
// These always report "unknown" rather than the actual error, for scripts.
- if (!strcmp(service, "get-serialno")) {
+ if (service == "get-serialno") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1159,9 +1192,9 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "get-devpath")) {
+ if (service == "get-devpath") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1169,9 +1202,9 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "get-state")) {
+ if (service == "get-state") {
std::string error;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
if (t) {
@@ -1179,39 +1212,46 @@
} else {
SendFail(reply_fd, error);
}
- return true;
+ return HostRequestResult::Handled;
}
// Indicates a new emulator instance has started.
- if (!strncmp(service, "emulator:", 9)) {
- int port = atoi(service+9);
- local_connect(port);
+ if (android::base::ConsumePrefix(&service, "emulator:")) {
+ unsigned int port;
+ if (!ParseUint(&port, service)) {
+ LOG(ERROR) << "received invalid port for emulator: " << service;
+ } else {
+ local_connect(port);
+ }
+
/* we don't even need to send a reply */
- return true;
+ return HostRequestResult::Handled;
}
- if (!strcmp(service, "reconnect")) {
+ if (service == "reconnect") {
std::string response;
atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
if (t != nullptr) {
- kick_transport(t);
+ kick_transport(t, true);
response =
- "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
+ "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
}
SendOkay(reply_fd, response);
- return true;
+ return HostRequestResult::Handled;
}
- if (handle_forward_request(service,
- [=](std::string* error) {
- return acquire_one_transport(type, serial, transport_id, nullptr,
- error);
- },
- reply_fd)) {
- return true;
+ // TODO: Switch handle_forward_request to string_view.
+ std::string service_str(service);
+ if (handle_forward_request(
+ service_str.c_str(),
+ [=](std::string* error) {
+ return acquire_one_transport(type, serial, transport_id, nullptr, error);
+ },
+ reply_fd)) {
+ return HostRequestResult::Handled;
}
- return false;
+ return HostRequestResult::Unhandled;
}
static auto& init_mutex = *new std::mutex();
diff --git a/adb/adb.h b/adb/adb.h
index 8c37c4b..352b2fe 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -26,7 +26,7 @@
#include <android-base/macros.h>
#include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "socket.h"
#include "types.h"
#include "usb.h"
@@ -107,6 +107,7 @@
kCsHost,
kCsRecovery,
kCsSideload,
+ kCsRescue,
};
inline bool ConnectionStateIsOnline(ConnectionState state) {
@@ -116,6 +117,7 @@
case kCsHost:
case kCsRecovery:
case kCsSideload:
+ case kCsRescue:
return true;
default:
return false;
@@ -139,13 +141,22 @@
atransport* find_emulator_transport_by_console_port(int console_port);
#endif
-int service_to_fd(const char* name, atransport* transport);
+unique_fd service_to_fd(std::string_view name, atransport* transport);
#if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id);
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+ TransportId transport_id);
+#endif
+
+#if !ADB_HOST
+asocket* daemon_service_to_socket(std::string_view name);
+#endif
+
+#if !ADB_HOST
+unique_fd execute_abb_command(std::string_view command);
#endif
#if !ADB_HOST
@@ -198,6 +209,9 @@
#define CHUNK_SIZE (64 * 1024)
+// Argument delimeter for adb abb command.
+#define ABB_ARG_DELIMETER ('\0')
+
#if !ADB_HOST
#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/"
#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH #x
@@ -207,8 +221,15 @@
#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2)
#endif
-bool handle_host_request(const char* service, TransportType type, const char* serial,
- TransportId transport_id, int reply_fd, asocket* s);
+enum class HostRequestResult {
+ Handled,
+ SwitchedTransport,
+ Unhandled,
+};
+
+HostRequestResult handle_host_request(std::string_view service, TransportType type,
+ const char* serial, TransportId transport_id, int reply_fd,
+ asocket* s);
void handle_online(atransport* t);
void handle_offline(atransport* t);
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 715e04f..2fc8478 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -36,6 +36,7 @@
void adb_auth_init();
int adb_auth_keygen(const char* filename);
+int adb_auth_pubkey(const char* filename);
std::string adb_auth_get_userkey();
std::deque<std::shared_ptr<RSA>> adb_auth_get_private_keys();
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 6cc274b..bdb8efa 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -20,6 +20,11 @@
#include <unistd.h>
+#if !ADB_HOST
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
#include <thread>
#include <android-base/stringprintf.h>
@@ -29,7 +34,7 @@
#include "adb_utils.h"
#include "sysdeps.h"
-bool SendProtocolString(int fd, const std::string& s) {
+bool SendProtocolString(borrowed_fd fd, std::string_view s) {
unsigned int length = s.size();
if (length > MAX_PAYLOAD - 4) {
errno = EMSGSIZE;
@@ -38,10 +43,11 @@
// The cost of sending two strings outweighs the cost of formatting.
// "adb sync" performance is affected by this.
- return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+ auto str = android::base::StringPrintf("%04x", length).append(s);
+ return WriteFdExactly(fd, str);
}
-bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error) {
char buf[5];
if (!ReadFdExactly(fd, buf, 4)) {
*error = perror_str("protocol fault (couldn't read status length)");
@@ -59,57 +65,57 @@
return true;
}
-bool SendOkay(int fd) {
+bool SendOkay(borrowed_fd fd) {
return WriteFdExactly(fd, "OKAY", 4);
}
-bool SendFail(int fd, const std::string& reason) {
+bool SendFail(borrowed_fd fd, std::string_view reason) {
return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
}
-bool ReadFdExactly(int fd, void* buf, size_t len) {
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len) {
char* p = reinterpret_cast<char*>(buf);
size_t len0 = len;
- D("readx: fd=%d wanted=%zu", fd, len);
+ D("readx: fd=%d wanted=%zu", fd.get(), len);
while (len > 0) {
int r = adb_read(fd, p, len);
if (r > 0) {
len -= r;
p += r;
} else if (r == -1) {
- D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
+ D("readx: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
return false;
} else {
- D("readx: fd=%d disconnected", fd);
+ D("readx: fd=%d disconnected", fd.get());
errno = 0;
return false;
}
}
- VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
- << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
+ VLOG(RWX) << "readx: fd=" << fd.get() << " wanted=" << len0 << " got=" << (len0 - len) << " "
+ << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
return true;
}
-bool WriteFdExactly(int fd, const void* buf, size_t len) {
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len) {
const char* p = reinterpret_cast<const char*>(buf);
int r;
- VLOG(RWX) << "writex: fd=" << fd << " len=" << len
- << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
+ VLOG(RWX) << "writex: fd=" << fd.get() << " len=" << len << " "
+ << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
while (len > 0) {
r = adb_write(fd, p, len);
if (r == -1) {
- D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
+ D("writex: fd=%d error %d: %s", fd.get(), errno, strerror(errno));
if (errno == EAGAIN) {
std::this_thread::yield();
continue;
} else if (errno == EPIPE) {
- D("writex: fd=%d disconnected", fd);
+ D("writex: fd=%d disconnected", fd.get());
errno = 0;
return false;
} else {
@@ -123,15 +129,15 @@
return true;
}
-bool WriteFdExactly(int fd, const char* str) {
+bool WriteFdExactly(borrowed_fd fd, const char* str) {
return WriteFdExactly(fd, str, strlen(str));
}
-bool WriteFdExactly(int fd, const std::string& str) {
+bool WriteFdExactly(borrowed_fd fd, const std::string& str) {
return WriteFdExactly(fd, str.c_str(), str.size());
}
-bool WriteFdFmt(int fd, const char* fmt, ...) {
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) {
std::string str;
va_list ap;
@@ -142,7 +148,7 @@
return WriteFdExactly(fd, str);
}
-bool ReadOrderlyShutdown(int fd) {
+bool ReadOrderlyShutdown(borrowed_fd fd) {
char buf[16];
// Only call this function if you're sure that the peer does
@@ -172,7 +178,7 @@
// data. We don't repeatedly call adb_read() until we get zero because
// we don't know how long that would take, but we do know that the
// caller wants to close the socket soon.
- VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+ VLOG(RWX) << "ReadOrderlyShutdown(" << fd.get() << ") unexpectedly read "
<< dump_hex(buf, result);
// Shutdown the socket to prevent the caller from reading or writing to
// it which doesn't make sense if we just read and discarded some data.
diff --git a/adb/adb_io.h b/adb/adb_io.h
index aa550af..9628946 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -20,18 +20,21 @@
#include <sys/types.h>
#include <string>
+#include <string_view>
+
+#include "adb_unique_fd.h"
// Sends the protocol "OKAY" message.
-bool SendOkay(int fd);
+bool SendOkay(borrowed_fd fd);
// Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(int fd, const std::string& reason);
+bool SendFail(borrowed_fd fd, std::string_view reason);
// Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(int fd, const std::string& s);
+bool SendProtocolString(borrowed_fd fd, std::string_view s);
// Reads a protocol-format string; a four hex digit length followed by the string data.
-bool ReadProtocolString(int fd, std::string* s, std::string* error);
+bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error);
// Reads exactly len bytes from fd into buf.
//
@@ -39,7 +42,7 @@
// were read. If EOF was found, errno will be set to 0.
//
// If this function fails, the contents of buf are undefined.
-bool ReadFdExactly(int fd, void* buf, size_t len);
+bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len);
// Given a client socket, wait for orderly/graceful shutdown. Call this:
//
@@ -57,20 +60,19 @@
// connect()s from the client to fail with WSAEADDRINUSE on Windows.
// Returns true if it is sure that orderly/graceful shutdown has occurred with
// no additional data read from the server.
-bool ReadOrderlyShutdown(int fd);
+bool ReadOrderlyShutdown(borrowed_fd fd);
// Writes exactly len bytes from buf to fd.
//
// Returns false if there is an error or if the fd was closed before the write
// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
// is closed, errno will be set to 0.
-bool WriteFdExactly(int fd, const void* buf, size_t len);
+bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len);
// Same as above, but for strings.
-bool WriteFdExactly(int fd, const char* s);
-bool WriteFdExactly(int fd, const std::string& s);
+bool WriteFdExactly(borrowed_fd fd, const char* s);
+bool WriteFdExactly(borrowed_fd fd, const std::string& s);
// Same as above, but formats the string to send.
-bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
+bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
#endif /* ADB_IO_H */
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 611b239..91b73a9 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -28,7 +28,6 @@
#include <string>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
// All of these tests fail on Windows because they use the C Runtime open(),
// but the adb_io APIs expect file descriptors from adb_open(). This could
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index 051ab73..29909a5 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -75,41 +75,36 @@
static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
if (ev & FDE_READ) {
- int fd = adb_socket_accept(_fd, nullptr, nullptr);
+ unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
if (fd < 0) return;
int rcv_buf_size = CHUNK_SIZE;
- adb_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
+ adb_setsockopt(fd.get(), SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(rcv_buf_size));
- asocket* s = create_local_socket(fd);
+ asocket* s = create_local_socket(std::move(fd));
if (s) {
connect_to_smartsocket(s);
return;
}
-
- adb_close(fd);
}
}
static void listener_event_func(int _fd, unsigned ev, void* _l)
{
alistener* listener = reinterpret_cast<alistener*>(_l);
- asocket *s;
if (ev & FDE_READ) {
- int fd = adb_socket_accept(_fd, nullptr, nullptr);
+ unique_fd fd(adb_socket_accept(_fd, nullptr, nullptr));
if (fd < 0) {
return;
}
- s = create_local_socket(fd);
+ asocket* s = create_local_socket(std::move(fd));
if (s) {
s->transport = listener->transport;
- connect_to_remote(s, listener->connect_to.c_str());
+ connect_to_remote(s, listener->connect_to);
return;
}
-
- adb_close(fd);
}
}
diff --git a/adb/adb_listeners_test.cpp b/adb/adb_listeners_test.cpp
index b697769..a7e2dea 100644
--- a/adb/adb_listeners_test.cpp
+++ b/adb/adb_listeners_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
#include "transport.h"
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index a8ec5fb..80f146c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -32,7 +32,9 @@
#if !ADB_HOST
const char* adb_device_banner = "device";
+#if defined(__ANDROID__)
static android::base::LogdLogger gLogdLogger;
+#endif
#else
const char* adb_device_banner = "host";
#endif
@@ -41,7 +43,12 @@
const char* tag, const char* file, unsigned int line,
const char* message) {
android::base::StderrLogger(id, severity, tag, file, line, message);
-#if !ADB_HOST
+#if defined(_WIN32)
+ // stderr can be buffered on Windows (and setvbuf doesn't seem to work), so explicitly flush.
+ fflush(stderr);
+#endif
+
+#if !ADB_HOST && defined(__ANDROID__)
// Only print logs of INFO or higher to logcat, so that `adb logcat` with adbd tracing on
// doesn't result in exponential logging.
if (severity >= android::base::INFO) {
@@ -67,8 +74,7 @@
}
void start_device_log(void) {
- int fd = unix_open(get_log_file_name().c_str(),
- O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+ int fd = unix_open(get_log_file_name(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
if (fd == -1) {
return;
}
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index d47213d..b6c910a 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -32,6 +32,8 @@
using unique_fd = android::base::unique_fd;
#endif
+using android::base::borrowed_fd;
+
template <typename T>
int adb_close(const android::base::unique_fd_impl<T>&)
__attribute__((__unavailable__("adb_close called on unique_fd")));
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 6960345..d1910f1 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -234,15 +234,15 @@
#if !defined(_WIN32)
// Windows version provided in sysdeps_win32.cpp
-bool set_file_block_mode(int fd, bool block) {
- int flags = fcntl(fd, F_GETFL, 0);
+bool set_file_block_mode(borrowed_fd fd, bool block) {
+ int flags = fcntl(fd.get(), F_GETFL, 0);
if (flags == -1) {
- PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+ PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd.get();
return false;
}
flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
- if (fcntl(fd, F_SETFL, flags) != 0) {
- PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+ if (fcntl(fd.get(), F_SETFL, flags) != 0) {
+ PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd.get() << ", flags " << flags;
return false;
}
return true;
@@ -312,7 +312,7 @@
std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
struct stat buf;
if (stat(android_dir.c_str(), &buf) == -1) {
- if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
+ if (adb_mkdir(android_dir, 0750) == -1) {
PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
}
}
@@ -320,6 +320,10 @@
}
std::string GetLogFilePath() {
+ // https://issuetracker.google.com/112588493
+ const char* path = getenv("ANDROID_ADB_LOG_PATH");
+ if (path) return path;
+
#if defined(_WIN32)
const char log_name[] = "adb.log";
WCHAR temp_path[MAX_PATH];
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 6d12225..faad03d 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,11 +19,14 @@
#include <condition_variable>
#include <mutex>
#include <string>
+#include <string_view>
+#include <type_traits>
#include <vector>
#include <android-base/macros.h>
#include "adb.h"
+#include "adb_unique_fd.h"
void close_stdin();
@@ -49,9 +52,7 @@
[[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
[[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
-bool set_file_block_mode(int fd, bool block);
-
-int adb_close(int fd);
+bool set_file_block_mode(borrowed_fd fd, bool block);
// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
// |error| and returns false.
@@ -94,3 +95,50 @@
};
std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+ size_t n = 0;
+ for (auto it = str.rbegin(); it != str.rend(); ++it) {
+ if (*it != '\0') {
+ break;
+ }
+ ++n;
+ }
+
+ str.remove_suffix(n);
+ return str;
+}
+
+// Base-10 stroll on a string_view.
+template <typename T>
+inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
+ if (str.empty() || !isdigit(str[0])) {
+ return false;
+ }
+
+ T value = 0;
+ std::string_view::iterator it;
+ constexpr T max = std::numeric_limits<T>::max();
+ for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
+ if (value > max / 10) {
+ return false;
+ }
+
+ value *= 10;
+
+ T digit = *it - '0';
+ if (value > max - digit) {
+ return false;
+ }
+
+ value += digit;
+ }
+ *result = value;
+ if (remaining) {
+ *remaining = str.substr(it - str.begin());
+ } else {
+ return it == str.end();
+ }
+
+ return true;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 341323f..cdca3aa 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -30,8 +30,8 @@
#include "sysdeps.h"
+#include <android-base/file.h>
#include <android-base/macros.h>
-#include <android-base/test_utils.h>
#ifdef _WIN32
static std::string subdir(const char* parent, const char* child) {
@@ -147,17 +147,16 @@
#if !defined(_WIN32)
TEST(adb_utils, set_file_block_mode) {
- int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
- ASSERT_GE(fd, 0);
- int flags = fcntl(fd, F_GETFL, 0);
- ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
- ASSERT_TRUE(set_file_block_mode(fd, false));
- int new_flags = fcntl(fd, F_GETFL, 0);
- ASSERT_EQ(flags | O_NONBLOCK, new_flags);
- ASSERT_TRUE(set_file_block_mode(fd, true));
- new_flags = fcntl(fd, F_GETFL, 0);
- ASSERT_EQ(flags, new_flags);
- ASSERT_EQ(0, adb_close(fd));
+ unique_fd fd(adb_open("/dev/null", O_RDWR | O_APPEND));
+ ASSERT_GE(fd, 0);
+ int flags = fcntl(fd.get(), F_GETFL, 0);
+ ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+ ASSERT_TRUE(set_file_block_mode(fd, false));
+ int new_flags = fcntl(fd.get(), F_GETFL, 0);
+ ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+ ASSERT_TRUE(set_file_block_mode(fd, true));
+ new_flags = fcntl(fd.get(), F_GETFL, 0);
+ ASSERT_EQ(flags, new_flags);
}
#endif
@@ -181,3 +180,56 @@
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:a", &error));
EXPECT_FALSE(forward_targets_are_valid("tcp:8000", "tcp:22x", &error));
}
+
+void TestParseUint(std::string_view string, bool expected_success, uint32_t expected_value = 0) {
+ // Standalone.
+ {
+ uint32_t value;
+ std::string_view remaining;
+ bool success = ParseUint(&value, string, &remaining);
+ EXPECT_EQ(success, expected_success);
+ if (expected_success) {
+ EXPECT_EQ(value, expected_value);
+ }
+ EXPECT_TRUE(remaining.empty());
+ }
+
+ // With trailing text.
+ {
+ std::string text = std::string(string) + "foo";
+ uint32_t value;
+ std::string_view remaining;
+ bool success = ParseUint(&value, text, &remaining);
+ EXPECT_EQ(success, expected_success);
+ if (expected_success) {
+ EXPECT_EQ(value, expected_value);
+ EXPECT_EQ(remaining, "foo");
+ }
+ }
+
+ // With trailing text, without remaining.
+ {
+ std::string text = std::string(string) + "foo";
+ uint32_t value;
+ bool success = ParseUint(&value, text, nullptr);
+ EXPECT_EQ(success, false);
+ }
+}
+
+TEST(adb_utils, ParseUint) {
+ TestParseUint("", false);
+ TestParseUint("foo", false);
+ TestParseUint("foo123", false);
+ TestParseUint("-1", false);
+
+ TestParseUint("123", true, 123);
+ TestParseUint("9999999999999999999999999", false);
+ TestParseUint(std::to_string(UINT32_MAX), true, UINT32_MAX);
+ TestParseUint("0" + std::to_string(UINT32_MAX), true, UINT32_MAX);
+ TestParseUint(std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+ TestParseUint("0" + std::to_string(static_cast<uint64_t>(UINT32_MAX) + 1), false);
+
+ std::string x = std::to_string(UINT32_MAX) + "123";
+ std::string_view substr = std::string_view(x).substr(0, std::to_string(UINT32_MAX).size());
+ TestParseUint(substr, true, UINT32_MAX);
+}
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
index e56ef5a..4d0cf49 100755
--- a/adb/benchmark_device.py
+++ b/adb/benchmark_device.py
@@ -17,6 +17,8 @@
import os
import statistics
+import subprocess
+import tempfile
import time
import adb
@@ -56,6 +58,41 @@
msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
print(msg % (name, len(speeds), median, mean, stddev))
+def benchmark_sink(device=None, size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ speeds = list()
+ cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
+
+ with tempfile.TemporaryFile() as tmpfile:
+ tmpfile.truncate(size_mb * 1024 * 1024)
+
+ for _ in range(0, 10):
+ tmpfile.seek(0)
+ begin = time.time()
+ subprocess.check_call(cmd, stdin=tmpfile)
+ end = time.time()
+ speeds.append(size_mb / float(end - begin))
+
+ analyze("sink %dMiB" % size_mb, speeds)
+
+def benchmark_source(device=None, size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ speeds = list()
+ cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
+
+ with open(os.devnull, 'w') as devnull:
+ for _ in range(0, 10):
+ begin = time.time()
+ subprocess.check_call(cmd, stdout=devnull)
+ end = time.time()
+ speeds.append(size_mb / float(end - begin))
+
+ analyze("source %dMiB" % size_mb, speeds)
+
def benchmark_push(device=None, file_size_mb=100):
if device == None:
device = adb.get_device()
@@ -110,6 +147,8 @@
def main():
device = adb.get_device()
unlock(device)
+ benchmark_sink(device)
+ benchmark_source(device)
benchmark_push(device)
benchmark_pull(device)
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index 72ca59a..a6be203 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -136,7 +136,7 @@
void ExpectBugreportzVersion(const std::string& version) {
EXPECT_CALL(br_, SendShellCommand("bugreportz -v", false, _))
- .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version.c_str())),
+ .WillOnce(DoAll(WithArg<2>(WriteOnStderr(version)),
WithArg<2>(ReturnCallbackDone(0))));
}
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index eda4b77..7e408a8 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -31,10 +31,12 @@
#include <condition_variable>
#include <mutex>
+#include <optional>
#include <string>
#include <thread>
#include <vector>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
@@ -70,51 +72,63 @@
__adb_server_socket_spec = socket_spec;
}
-static int switch_socket_transport(int fd, std::string* error) {
+static std::optional<TransportId> switch_socket_transport(int fd, std::string* error) {
+ TransportId result;
+ bool read_transport = true;
+
std::string service;
if (__adb_transport_id) {
+ read_transport = false;
service += "host:transport-id:";
service += std::to_string(__adb_transport_id);
+ result = __adb_transport_id;
} else if (__adb_serial) {
- service += "host:transport:";
+ service += "host:tport:serial:";
service += __adb_serial;
} else {
const char* transport_type = "???";
switch (__adb_transport) {
case kTransportUsb:
- transport_type = "transport-usb";
- break;
+ transport_type = "usb";
+ break;
case kTransportLocal:
- transport_type = "transport-local";
- break;
+ transport_type = "local";
+ break;
case kTransportAny:
- transport_type = "transport-any";
- break;
+ transport_type = "any";
+ break;
case kTransportHost:
// no switch necessary
return 0;
}
- service += "host:";
+ service += "host:tport:";
service += transport_type;
}
if (!SendProtocolString(fd, service)) {
*error = perror_str("write failure during connection");
- adb_close(fd);
- return -1;
+ return std::nullopt;
}
- D("Switch transport in progress");
+
+ LOG(DEBUG) << "Switch transport in progress: " << service;
if (!adb_status(fd, error)) {
- adb_close(fd);
D("Switch transport failed: %s", error->c_str());
- return -1;
+ return std::nullopt;
}
+
+ if (read_transport) {
+ if (!ReadFdExactly(fd, &result, sizeof(result))) {
+ *error = "failed to read transport id from server";
+ return std::nullopt;
+ }
+ }
+
D("Switch transport success");
- return 0;
+ return result;
}
-bool adb_status(int fd, std::string* error) {
+bool adb_status(borrowed_fd fd, std::string* error) {
char buf[5];
if (!ReadFdExactly(fd, buf, 4)) {
*error = perror_str("protocol fault (couldn't read status)");
@@ -135,74 +149,93 @@
return false;
}
-static int _adb_connect(const std::string& service, std::string* error) {
- D("_adb_connect: %s", service.c_str());
+static int _adb_connect(std::string_view service, TransportId* transport, std::string* error) {
+ LOG(DEBUG) << "_adb_connect: " << service;
if (service.empty() || service.size() > MAX_PAYLOAD) {
- *error = android::base::StringPrintf("bad service name length (%zd)",
- service.size());
+ *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
return -1;
}
std::string reason;
- int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
- if (fd < 0) {
+ unique_fd fd;
+ if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
*error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
__adb_server_socket_spec, reason.c_str());
return -2;
}
- if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
- return -1;
+ if (!service.starts_with("host")) {
+ std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
+ if (!transport_result) {
+ return -1;
+ }
+
+ if (transport) {
+ *transport = *transport_result;
+ }
}
- if (!SendProtocolString(fd, service)) {
+ if (!SendProtocolString(fd.get(), service)) {
*error = perror_str("write failure during connection");
- adb_close(fd);
return -1;
}
- if (!adb_status(fd, error)) {
- adb_close(fd);
+ if (!adb_status(fd.get(), error)) {
return -1;
}
- D("_adb_connect: return fd %d", fd);
- return fd;
+ D("_adb_connect: return fd %d", fd.get());
+ return fd.release();
}
bool adb_kill_server() {
D("adb_kill_server");
std::string reason;
- int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
- if (fd < 0) {
+ unique_fd fd;
+ if (!socket_spec_connect(&fd, __adb_server_socket_spec, nullptr, nullptr, &reason)) {
fprintf(stderr, "cannot connect to daemon at %s: %s\n", __adb_server_socket_spec,
reason.c_str());
return true;
}
- if (!SendProtocolString(fd, "host:kill")) {
+ if (!SendProtocolString(fd.get(), "host:kill")) {
fprintf(stderr, "error: write failure during connection: %s\n", strerror(errno));
return false;
}
// The server might send OKAY, so consume that.
char buf[4];
- ReadFdExactly(fd, buf, 4);
+ ReadFdExactly(fd.get(), buf, 4);
// Now that no more data is expected, wait for socket orderly shutdown or error, indicating
// server death.
- ReadOrderlyShutdown(fd);
+ ReadOrderlyShutdown(fd.get());
return true;
}
-int adb_connect(const std::string& service, std::string* error) {
- // first query the adb server's version
- int fd = _adb_connect("host:version", error);
+int adb_connect(std::string_view service, std::string* error) {
+ return adb_connect(nullptr, service, error);
+}
- D("adb_connect: service %s", service.c_str());
- if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
+#if defined(__linux__)
+std::optional<std::string> adb_get_server_executable_path() {
+ int port;
+ std::string error;
+ if (!parse_tcp_socket_spec(__adb_server_socket_spec, nullptr, &port, nullptr, &error)) {
+ LOG(FATAL) << "failed to parse server socket spec: " << error;
+ }
+
+ return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adb." + std::to_string(port);
+}
+#endif
+
+static bool __adb_check_server_version(std::string* error) {
+ unique_fd fd(_adb_connect("host:version", nullptr, error));
+
+ bool local = is_local_socket_spec(__adb_server_socket_spec);
+ if (fd == -2 && !local) {
fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
- return fd;
+ return false;
} else if (fd == -2) {
fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
start_server:
@@ -212,7 +245,7 @@
// return a generic error string about the overall adb_connect()
// that the caller requested.
*error = "cannot connect to daemon";
- return -1;
+ return false;
} else {
fprintf(stderr, "* daemon started successfully\n");
}
@@ -220,34 +253,53 @@
// Fall through to _adb_connect.
} else {
// If a server is already running, check its version matches.
- int version = ADB_SERVER_VERSION - 1;
+ int version = 0;
// If we have a file descriptor, then parse version result.
if (fd >= 0) {
std::string version_string;
if (!ReadProtocolString(fd, &version_string, error)) {
- adb_close(fd);
- return -1;
+ return false;
}
ReadOrderlyShutdown(fd);
- adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
*error = android::base::StringPrintf("cannot parse version string: %s",
version_string.c_str());
- return -1;
+ return false;
}
} else {
// If fd is -1 check for "unknown host service" which would
// indicate a version of adb that does not support the
// version command, in which case we should fall-through to kill it.
if (*error != "unknown host service") {
- return fd;
+ return false;
}
}
if (version != ADB_SERVER_VERSION) {
+#if defined(__linux__)
+ if (version > ADB_SERVER_VERSION && local) {
+ // Try to re-exec the existing adb server's binary.
+ constexpr const char* adb_reexeced = "adb (re-execed)";
+ if (strcmp(adb_reexeced, *__adb_argv) != 0) {
+ __adb_argv[0] = adb_reexeced;
+ std::optional<std::string> server_path_path = adb_get_server_executable_path();
+ std::string server_path;
+ if (server_path_path &&
+ android::base::ReadFileToString(*server_path_path, &server_path)) {
+ if (execve(server_path.c_str(), const_cast<char**>(__adb_argv),
+ const_cast<char**>(__adb_envp)) == -1) {
+ LOG(ERROR) << "failed to exec newer version at " << server_path;
+ }
+
+ // Fall-through to restarting the server.
+ }
+ }
+ }
+#endif
+
fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
version, ADB_SERVER_VERSION);
adb_kill_server();
@@ -255,57 +307,76 @@
}
}
+ return true;
+}
+
+bool adb_check_server_version(std::string* error) {
+ // Only check the version once per process, since this isn't atomic anyway.
+ static std::once_flag once;
+ static bool result;
+ static std::string* err;
+ std::call_once(once, []() {
+ err = new std::string();
+ result = __adb_check_server_version(err);
+ });
+ *error = *err;
+ return result;
+}
+
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+ LOG(DEBUG) << "adb_connect: service: " << service;
+
+ // Query the adb server's version.
+ if (!adb_check_server_version(error)) {
+ return -1;
+ }
+
// if the command is start-server, we are done.
if (service == "host:start-server") {
return 0;
}
- fd = _adb_connect(service, error);
+ unique_fd fd(_adb_connect(service, transport, error));
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
fprintf(stderr, "* daemon still not running\n");
}
- D("adb_connect: return fd %d", fd);
+ D("adb_connect: return fd %d", fd.get());
- return fd;
+ return fd.release();
}
-
bool adb_command(const std::string& service) {
std::string error;
- int fd = adb_connect(service, &error);
+ unique_fd fd(adb_connect(service, &error));
if (fd < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
return false;
}
- if (!adb_status(fd, &error)) {
+ if (!adb_status(fd.get(), &error)) {
fprintf(stderr, "error: %s\n", error.c_str());
- adb_close(fd);
return false;
}
- ReadOrderlyShutdown(fd);
- adb_close(fd);
+ ReadOrderlyShutdown(fd.get());
return true;
}
bool adb_query(const std::string& service, std::string* result, std::string* error) {
D("adb_query: %s", service.c_str());
- int fd = adb_connect(service, error);
+ unique_fd fd(adb_connect(service, error));
if (fd < 0) {
return false;
}
result->clear();
- if (!ReadProtocolString(fd, result, error)) {
- adb_close(fd);
+ if (!ReadProtocolString(fd.get(), result, error)) {
return false;
}
- ReadOrderlyShutdown(fd);
- adb_close(fd);
+ ReadOrderlyShutdown(fd.get());
return true;
}
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index d467539..fe1e584 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -16,15 +16,25 @@
#pragma once
+#include <optional>
+#include <string>
+
#include "adb.h"
+#include "adb_unique_fd.h"
#include "sysdeps.h"
#include "transport.h"
-#include <string>
+// Explicitly check the adb server version.
+// All of the commands below do this implicitly.
+// Only the first invocation of this function will check the server version.
+bool adb_check_server_version(std::string* _Nonnull error);
// Connect to adb, connect to the named service, and return a valid fd for
// interacting with that service upon success or a negative number on failure.
-int adb_connect(const std::string& service, std::string* _Nonnull error);
+int adb_connect(std::string_view service, std::string* _Nonnull error);
+
+// Same as above, except returning the TransportId for the service that we've connected to.
+int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error);
// Kill the currently running adb server, if it exists.
bool adb_kill_server();
@@ -55,10 +65,20 @@
// Reads a standard adb status response (OKAY|FAIL) and returns true in the
// event of OKAY, false in the event of FAIL or protocol error.
-bool adb_status(int fd, std::string* _Nonnull error);
+bool adb_status(borrowed_fd fd, std::string* _Nonnull error);
// Create a host command corresponding to selected transport type/serial.
std::string format_host_command(const char* _Nonnull command);
// Get the feature set of the current preferred transport.
bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
+
+#if defined(__linux__)
+// Get the path of a file containing the path to the server executable, if the socket spec set via
+// adb_set_socket_spec is a local one.
+std::optional<std::string> adb_get_server_executable_path();
+#endif
+
+// Globally acccesible argv/envp, for the purpose of re-execing adb.
+extern const char* _Nullable * _Nullable __adb_argv;
+extern const char* _Nullable * _Nullable __adb_envp;
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 0008f72..16fa215 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -24,19 +24,21 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
#include "adb.h"
#include "adb_client.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
-#include "android-base/file.h"
-#include "android-base/stringprintf.h"
-#include "android-base/strings.h"
-#include "android-base/test_utils.h"
#include "client/file_sync_client.h"
#include "commandline.h"
#include "fastdeploy.h"
-#include "sysdeps.h"
+#if defined(ENABLE_FASTDEPLOY)
static constexpr int kFastDeployMinApi = 24;
+#endif
static bool can_use_feature(const char* feature) {
FeatureSet features;
@@ -130,10 +132,12 @@
*buf = '\0';
}
+#if defined(ENABLE_FASTDEPLOY)
static int delete_device_patch_file(const char* apkPath) {
std::string patchDevicePath = get_patch_path(apkPath);
return delete_device_file(patchDevicePath);
}
+#endif
static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
bool use_localagent) {
@@ -159,20 +163,30 @@
}
if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
TemporaryFile metadataTmpFile;
- TemporaryFile patchTmpFile;
+ std::string patchTmpFilePath;
+ {
+ TemporaryFile patchTmpFile;
+ patchTmpFile.DoNotRemove();
+ patchTmpFilePath = patchTmpFile.path;
+ }
FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
extract_metadata(file, metadataFile);
fclose(metadataFile);
- create_patch(file, metadataTmpFile.path, patchTmpFile.path);
+ create_patch(file, metadataTmpFile.path, patchTmpFilePath.c_str());
// pass all but 1st (command) and last (apk path) parameters through to pm for
// session creation
std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
- install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
+ install_patch(file, patchTmpFilePath.c_str(), pm_args.size(), pm_args.data());
+ adb_unlink(patchTmpFilePath.c_str());
delete_device_patch_file(file);
return 0;
+#else
+ error_exit("fastdeploy is disabled");
+#endif
} else {
struct stat sb;
if (stat(file, &sb) == -1) {
@@ -180,8 +194,8 @@
return 1;
}
- int localFd = adb_open(file, O_RDONLY);
- if (localFd < 0) {
+ unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+ if (local_fd < 0) {
fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
return 1;
}
@@ -202,19 +216,15 @@
cmd += " --apex";
}
- int remoteFd = adb_connect(cmd, &error);
- if (remoteFd < 0) {
+ unique_fd remote_fd(adb_connect(cmd, &error));
+ if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
- adb_close(localFd);
return 1;
}
char buf[BUFSIZ];
- copy_to_file(localFd, remoteFd);
- read_status_line(remoteFd, buf, sizeof(buf));
-
- adb_close(localFd);
- adb_close(remoteFd);
+ copy_to_file(local_fd.get(), remote_fd.get());
+ read_status_line(remote_fd.get(), buf, sizeof(buf));
if (!strncmp("Success", buf, 7)) {
fputs(buf, stdout);
@@ -227,18 +237,8 @@
static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
bool use_localagent) {
- static const char* const DATA_DEST = "/data/local/tmp/%s";
- static const char* const SD_DEST = "/sdcard/tmp/%s";
- const char* where = DATA_DEST;
-
printf("Performing Push Install\n");
- for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "-s")) {
- where = SD_DEST;
- }
- }
-
// Find last APK argument.
// All other arguments passed through verbatim.
int last_apk = -1;
@@ -257,9 +257,10 @@
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest =
- android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
+ "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
TemporaryFile metadataTmpFile;
TemporaryFile patchTmpFile;
@@ -269,6 +270,9 @@
create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
+#else
+ error_exit("fastdeploy is disabled");
+#endif
} else {
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
}
@@ -278,7 +282,9 @@
cleanup_apk:
if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
delete_device_patch_file(apk_file[0]);
+#endif
}
delete_device_file(apk_dest);
return result;
@@ -342,17 +348,14 @@
error_exit("Attempting to use streaming install on unsupported device");
}
- if (use_fastdeploy == true && is_reinstall == false) {
- printf("Fast Deploy is only available with -r.\n");
- use_fastdeploy = false;
- }
-
+#if defined(ENABLE_FASTDEPLOY)
if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
printf("Fast Deploy is only compatible with devices of API version %d or higher, "
"ignoring.\n",
kFastDeployMinApi);
use_fastdeploy = false;
}
+#endif
std::vector<const char*> passthrough_argv;
for (int i = 0; i < argc; i++) {
@@ -361,10 +364,21 @@
passthrough_argv.push_back(argv[i]);
}
}
+ if (passthrough_argv.size() < 2) {
+ error_exit("install requires an apk argument");
+ }
if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
fastdeploy_set_local_agent(use_localagent);
update_agent(agent_update_strategy);
+
+ // The last argument must be the APK file
+ const char* file = passthrough_argv.back();
+ use_fastdeploy = find_package(file);
+#else
+ error_exit("fastdeploy is disabled");
+#endif
}
switch (installMode) {
@@ -419,14 +433,15 @@
// Create install session
std::string error;
- int fd = adb_connect(cmd, &error);
- if (fd < 0) {
- fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
- return EXIT_FAILURE;
- }
char buf[BUFSIZ];
- read_status_line(fd, buf, sizeof(buf));
- adb_close(fd);
+ {
+ unique_fd fd(adb_connect(cmd, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
int session_id = -1;
if (!strncmp("Success", buf, 7)) {
@@ -459,27 +474,23 @@
install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
session_id, android::base::Basename(file).c_str());
- int localFd = adb_open(file, O_RDONLY);
- if (localFd < 0) {
+ unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
+ if (local_fd < 0) {
fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
std::string error;
- int remoteFd = adb_connect(cmd, &error);
- if (remoteFd < 0) {
+ unique_fd remote_fd(adb_connect(cmd, &error));
+ if (remote_fd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
- adb_close(localFd);
success = 0;
goto finalize_session;
}
- copy_to_file(localFd, remoteFd);
- read_status_line(remoteFd, buf, sizeof(buf));
-
- adb_close(localFd);
- adb_close(remoteFd);
+ copy_to_file(local_fd.get(), remote_fd.get());
+ read_status_line(remote_fd.get(), buf, sizeof(buf));
if (strncmp("Success", buf, 7)) {
fprintf(stderr, "adb: failed to write %s\n", file);
@@ -493,13 +504,14 @@
// Commit session if we streamed everything okay; otherwise abandon
std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
success ? "commit" : "abandon", session_id);
- fd = adb_connect(service, &error);
- if (fd < 0) {
- fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
- return EXIT_FAILURE;
+ {
+ unique_fd fd(adb_connect(service, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
}
- read_status_line(fd, buf, sizeof(buf));
- adb_close(fd);
if (!strncmp("Success", buf, 7)) {
fputs(buf, stdout);
@@ -510,6 +522,222 @@
return EXIT_FAILURE;
}
+int install_multi_package(int argc, const char** argv) {
+ // Find all APK arguments starting at end.
+ // All other arguments passed through verbatim.
+ bool apex_found = false;
+ int first_package = -1;
+ for (int i = argc - 1; i >= 0; i--) {
+ const char* file = argv[i];
+ if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+ android::base::EndsWithIgnoreCase(file, ".apex")) {
+ first_package = i;
+ if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+ apex_found = true;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (first_package == -1) error_exit("need APK or APEX files on command line");
+
+ if (use_legacy_install()) {
+ fprintf(stderr, "adb: multi-package install is not supported on this device\n");
+ return EXIT_FAILURE;
+ }
+ std::string install_cmd = "exec:cmd package";
+
+ std::string multi_package_cmd =
+ android::base::StringPrintf("%s install-create --multi-package", install_cmd.c_str());
+ for (int i = 1; i < first_package; i++) {
+ multi_package_cmd += " " + escape_arg(argv[i]);
+ }
+
+ if (apex_found) {
+ multi_package_cmd += " --staged";
+ }
+
+ // Create multi-package install session
+ std::string error;
+ char buf[BUFSIZ];
+ {
+ unique_fd fd(adb_connect(multi_package_cmd, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for create multi-package: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
+
+ int parent_session_id = -1;
+ if (!strncmp("Success", buf, 7)) {
+ char* start = strrchr(buf, '[');
+ char* end = strrchr(buf, ']');
+ if (start && end) {
+ *end = '\0';
+ parent_session_id = strtol(start + 1, nullptr, 10);
+ }
+ }
+ if (parent_session_id < 0) {
+ fprintf(stderr, "adb: failed to create multi-package session\n");
+ fputs(buf, stderr);
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stdout, "Created parent session ID %d.\n", parent_session_id);
+
+ std::vector<int> session_ids;
+
+ // Valid session, now create the individual sessions and stream the APKs
+ int success = EXIT_FAILURE;
+ std::string individual_cmd =
+ android::base::StringPrintf("%s install-create", install_cmd.c_str());
+ std::string all_session_ids = "";
+ for (int i = 1; i < first_package; i++) {
+ individual_cmd += " " + escape_arg(argv[i]);
+ }
+ if (apex_found) {
+ individual_cmd += " --staged";
+ }
+ std::string individual_apex_cmd = individual_cmd + " --apex";
+ std::string cmd = "";
+ for (int i = first_package; i < argc; i++) {
+ const char* file = argv[i];
+ char buf[BUFSIZ];
+ {
+ unique_fd fd;
+ // Create individual install session
+ if (android::base::EndsWithIgnoreCase(file, ".apex")) {
+ fd.reset(adb_connect(individual_apex_cmd, &error));
+ } else {
+ fd.reset(adb_connect(individual_cmd, &error));
+ }
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+ goto finalize_multi_package_session;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
+
+ int session_id = -1;
+ if (!strncmp("Success", buf, 7)) {
+ char* start = strrchr(buf, '[');
+ char* end = strrchr(buf, ']');
+ if (start && end) {
+ *end = '\0';
+ session_id = strtol(start + 1, nullptr, 10);
+ }
+ }
+ if (session_id < 0) {
+ fprintf(stderr, "adb: failed to create multi-package session\n");
+ fputs(buf, stderr);
+ goto finalize_multi_package_session;
+ }
+
+ fprintf(stdout, "Created child session ID %d.\n", session_id);
+ session_ids.push_back(session_id);
+
+ // Support splitAPKs by allowing the notation split1.apk:split2.apk:split3.apk as argument.
+ std::vector<std::string> splits = android::base::Split(file, ":");
+
+ for (const std::string& split : splits) {
+ struct stat sb;
+ if (stat(split.c_str(), &sb) == -1) {
+ fprintf(stderr, "adb: failed to stat %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string cmd = android::base::StringPrintf(
+ "%s install-write -S %" PRIu64 " %d %d_%s -", install_cmd.c_str(),
+ static_cast<uint64_t>(sb.st_size), session_id, i,
+ android::base::Basename(split).c_str());
+
+ unique_fd local_fd(adb_open(split.c_str(), O_RDONLY | O_CLOEXEC));
+ if (local_fd < 0) {
+ fprintf(stderr, "adb: failed to open %s: %s\n", split.c_str(), strerror(errno));
+ goto finalize_multi_package_session;
+ }
+
+ std::string error;
+ unique_fd remote_fd(adb_connect(cmd, &error));
+ if (remote_fd < 0) {
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+ goto finalize_multi_package_session;
+ }
+
+ copy_to_file(local_fd.get(), remote_fd.get());
+ read_status_line(remote_fd.get(), buf, sizeof(buf));
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "adb: failed to write %s\n", split.c_str());
+ fputs(buf, stderr);
+ goto finalize_multi_package_session;
+ }
+ }
+ all_session_ids += android::base::StringPrintf(" %d", session_id);
+ }
+
+ cmd = android::base::StringPrintf("%s install-add-session %d%s", install_cmd.c_str(),
+ parent_session_id, all_session_ids.c_str());
+ {
+ unique_fd fd(adb_connect(cmd, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for install-add-session: %s\n", error.c_str());
+ goto finalize_multi_package_session;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
+
+ if (strncmp("Success", buf, 7)) {
+ fprintf(stderr, "adb: failed to link sessions (%s)\n", cmd.c_str());
+ fputs(buf, stderr);
+ goto finalize_multi_package_session;
+ }
+
+ // no failures means we can proceed with the assumption of success
+ success = 0;
+
+finalize_multi_package_session:
+ // Commit session if we streamed everything okay; otherwise abandon
+ std::string service =
+ android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
+ success == 0 ? "commit" : "abandon", parent_session_id);
+ {
+ unique_fd fd(adb_connect(service, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+ return EXIT_FAILURE;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
+
+ if (!strncmp("Success", buf, 7)) {
+ fputs(buf, stdout);
+ if (success == 0) {
+ return 0;
+ }
+ } else {
+ fprintf(stderr, "adb: failed to finalize session\n");
+ fputs(buf, stderr);
+ }
+
+ session_ids.push_back(parent_session_id);
+ // try to abandon all remaining sessions
+ for (std::size_t i = 0; i < session_ids.size(); i++) {
+ service = android::base::StringPrintf("%s install-abandon %d", install_cmd.c_str(),
+ session_ids[i]);
+ fprintf(stderr, "Attempting to abandon session ID %d\n", session_ids[i]);
+ unique_fd fd(adb_connect(service, &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+ continue;
+ }
+ read_status_line(fd.get(), buf, sizeof(buf));
+ }
+ return EXIT_FAILURE;
+}
+
int delete_device_file(const std::string& filename) {
std::string cmd = "rm -f " + escape_arg(filename);
return send_shell_command(cmd);
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
index 5b6c4cb..9946604 100644
--- a/adb/client/adb_install.h
+++ b/adb/client/adb_install.h
@@ -20,6 +20,7 @@
int install_app(int argc, const char** argv);
int install_multiple_app(int argc, const char** argv);
+int install_multi_package(int argc, const char** argv);
int uninstall_app(int argc, const char** argv);
int delete_device_file(const std::string& filename);
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index 71c19b8..ed6a9a8 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -43,6 +43,7 @@
#include "adb.h"
#include "adb_auth.h"
+#include "adb_io.h"
#include "adb_utils.h"
#include "sysdeps.h"
#include "transport.h"
@@ -53,8 +54,6 @@
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
static std::string get_user_info() {
- LOG(INFO) << "get_user_info...";
-
std::string hostname;
if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
#if !defined(_WIN32)
@@ -65,7 +64,7 @@
std::string username;
if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET
+#if !defined(_WIN32)
if (username.empty() && getlogin()) username = getlogin();
#endif
if (username.empty()) hostname = "unknown";
@@ -73,9 +72,7 @@
return " " + username + "@" + hostname;
}
-static bool write_public_keyfile(RSA* private_key, const std::string& private_key_path) {
- LOG(INFO) << "write_public_keyfile...";
-
+static bool calculate_public_key(std::string* out, RSA* private_key) {
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
LOG(ERROR) << "Failed to convert to public key";
@@ -88,20 +85,11 @@
return false;
}
- std::string content;
- content.resize(expected_length);
- size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(&content[0]), binary_key_data,
+ out->resize(expected_length);
+ size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
- content.resize(actual_length);
-
- content += get_user_info();
-
- std::string path(private_key_path + ".pub");
- if (!android::base::WriteStringToFile(content, path)) {
- PLOG(ERROR) << "Failed to write public key to '" << path << "'";
- return false;
- }
-
+ out->resize(actual_length);
+ out->append(get_user_info());
return true;
}
@@ -111,6 +99,7 @@
mode_t old_mask;
FILE *f = nullptr;
int ret = 0;
+ std::string pubkey;
EVP_PKEY* pkey = EVP_PKEY_new();
BIGNUM* exponent = BN_new();
@@ -124,6 +113,11 @@
RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
EVP_PKEY_set1_RSA(pkey, rsa);
+ if (!calculate_public_key(&pubkey, rsa)) {
+ LOG(ERROR) << "failed to calculate public key";
+ goto out;
+ }
+
old_mask = umask(077);
f = fopen(file.c_str(), "w");
@@ -136,12 +130,12 @@
umask(old_mask);
if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
- D("Failed to write key");
+ LOG(ERROR) << "Failed to write key";
goto out;
}
- if (!write_public_keyfile(rsa, file)) {
- D("Failed to write public key");
+ if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
+ PLOG(ERROR) << "failed to write public key";
goto out;
}
@@ -170,36 +164,41 @@
return result;
}
-static bool read_key_file(const std::string& file) {
- LOG(INFO) << "read_key_file '" << file << "'...";
-
+static std::shared_ptr<RSA> read_key_file(const std::string& file) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file.c_str(), "r"), fclose);
if (!fp) {
PLOG(ERROR) << "Failed to open '" << file << "'";
- return false;
+ return nullptr;
}
RSA* key = RSA_new();
if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
LOG(ERROR) << "Failed to read key";
RSA_free(key);
+ return nullptr;
+ }
+
+ return std::shared_ptr<RSA>(key, RSA_free);
+}
+
+static bool load_key(const std::string& file) {
+ std::shared_ptr<RSA> key = read_key_file(file);
+ if (!key) {
return false;
}
std::lock_guard<std::mutex> lock(g_keys_mutex);
- std::string fingerprint = hash_key(key);
+ std::string fingerprint = hash_key(key.get());
if (g_keys.find(fingerprint) != g_keys.end()) {
LOG(INFO) << "ignoring already-loaded key: " << file;
- RSA_free(key);
} else {
- g_keys[fingerprint] = std::shared_ptr<RSA>(key, RSA_free);
+ g_keys[fingerprint] = std::move(key);
}
-
return true;
}
-static bool read_keys(const std::string& path, bool allow_dir = true) {
- LOG(INFO) << "read_keys '" << path << "'...";
+static bool load_keys(const std::string& path, bool allow_dir = true) {
+ LOG(INFO) << "load_keys '" << path << "'...";
struct stat st;
if (stat(path.c_str(), &st) != 0) {
@@ -208,7 +207,7 @@
}
if (S_ISREG(st.st_mode)) {
- return read_key_file(path);
+ return load_key(path);
} else if (S_ISDIR(st.st_mode)) {
if (!allow_dir) {
// inotify isn't recursive. It would break expectations to load keys in nested
@@ -237,7 +236,7 @@
continue;
}
- result |= read_key_file((path + OS_PATH_SEPARATOR + name));
+ result |= load_key((path + OS_PATH_SEPARATOR + name));
}
return result;
}
@@ -250,7 +249,7 @@
return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
}
-static bool get_user_key() {
+static bool generate_userkey() {
std::string path = get_user_key_path();
if (path.empty()) {
PLOG(ERROR) << "Error getting user key filename";
@@ -266,7 +265,7 @@
}
}
- return read_key_file(path);
+ return load_key(path);
}
static std::set<std::string> get_vendor_keys() {
@@ -320,26 +319,42 @@
return result;
}
+static bool pubkey_from_privkey(std::string* out, const std::string& path) {
+ std::shared_ptr<RSA> privkey = read_key_file(path);
+ if (!privkey) {
+ return false;
+ }
+ return calculate_public_key(out, privkey.get());
+}
+
std::string adb_auth_get_userkey() {
std::string path = get_user_key_path();
if (path.empty()) {
PLOG(ERROR) << "Error getting user key filename";
return "";
}
- path += ".pub";
- std::string content;
- if (!android::base::ReadFileToString(path, &content)) {
- PLOG(ERROR) << "Can't load '" << path << "'";
+ std::string result;
+ if (!pubkey_from_privkey(&result, path)) {
return "";
}
- return content;
+ return result;
}
int adb_auth_keygen(const char* filename) {
return (generate_key(filename) == 0);
}
+int adb_auth_pubkey(const char* filename) {
+ std::string pubkey;
+ if (!pubkey_from_privkey(&pubkey, filename)) {
+ return 1;
+ }
+ pubkey.push_back('\n');
+
+ return WriteFdExactly(STDOUT_FILENO, pubkey.data(), pubkey.size()) ? 0 : 1;
+}
+
#if defined(__linux__)
static void adb_auth_inotify_update(int fd, unsigned fd_event, void*) {
LOG(INFO) << "adb_auth_inotify_update called";
@@ -380,7 +395,7 @@
LOG(INFO) << "ignoring new directory at '" << path << "'";
} else {
LOG(INFO) << "observed new file at '" << path << "'";
- read_keys(path, false);
+ load_keys(path, false);
}
} else {
LOG(WARNING) << "unmonitored event for " << path << ": 0x" << std::hex
@@ -420,8 +435,8 @@
void adb_auth_init() {
LOG(INFO) << "adb_auth_init...";
- if (!get_user_key()) {
- LOG(ERROR) << "Failed to get user key";
+ if (!generate_userkey()) {
+ LOG(ERROR) << "Failed to generate user key";
return;
}
@@ -432,7 +447,7 @@
#endif
for (const std::string& path : key_paths) {
- read_keys(path.c_str());
+ load_keys(path);
}
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index b5bed28..3c03eb2 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -96,9 +96,8 @@
" version show version num\n"
"\n"
"networking:\n"
- " connect HOST[:PORT] connect to a device via TCP/IP [default port=5555]\n"
- " disconnect [HOST[:PORT]]\n"
- " disconnect from given TCP/IP device [default port=5555], or all\n"
+ " connect HOST[:PORT] connect to a device via TCP/IP\n"
+ " disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\n"
" forward --list list all forward socket connections\n"
" forward [--no-rebind] LOCAL REMOTE\n"
" forward socket connection using:\n"
@@ -128,9 +127,9 @@
" pull [-a] REMOTE... LOCAL\n"
" copy files/dirs from device\n"
" -a: preserve file timestamp and mode\n"
- " sync [all|data|odm|oem|product_services|product|system|vendor]\n"
+ " sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
" sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
- " -l: list but don't copy\n"
+ " -l: list files that would be copied, but don't copy them\n"
"\n"
"shell:\n"
" shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -142,22 +141,23 @@
" -x: disable remote exit codes and stdout/stderr separation\n"
" emu COMMAND run emulator console command\n"
"\n"
- "app installation:\n"
+ "app installation (see also `adb shell cmd package help`):\n"
" install [-lrtsdg] [--instant] PACKAGE\n"
+ " push a single package to the device and install it\n"
" install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
- " push package(s) to the device and install them\n"
- " -l: forward lock application\n"
+ " push multiple APKs to the device for a single package and install them\n"
+ " install-multi-package [-lrtsdpg] [--instant] PACKAGE...\n"
+ " push one or more packages to the device and install them atomically\n"
" -r: replace existing application\n"
" -t: allow test packages\n"
- " -s: install application on sdcard\n"
" -d: allow version code downgrade (debuggable packages only)\n"
" -p: partial application install (install-multiple only)\n"
" -g: grant all runtime permissions\n"
" --instant: cause the app to be installed as an ephemeral install app\n"
" --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
" --streaming: force streaming APK directly into Package Manager\n"
- " --fastdeploy: use fast deploy (only valid with -r)\n"
- " --no-fastdeploy: prevent use of fast deploy (only valid with -r)\n"
+ " --fastdeploy: use fast deploy\n"
+ " --no-fastdeploy: prevent use of fast deploy\n"
" --force-agent: force update of deployment agent when using fast deploy\n"
" --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
" --version-check-agent: update deployment agent when local version has different version code and using fast deploy\n"
@@ -169,9 +169,6 @@
" remove this app package from the device\n"
" '-k': keep the data and cache directories\n"
"\n"
- "backup/restore:\n"
- " to show usage run \"adb shell bu help\"\n"
- "\n"
"debugging:\n"
" bugreport [PATH]\n"
" write bugreport to given PATH [default=bugreport.zip];\n"
@@ -185,17 +182,18 @@
" enable-verity re-enable dm-verity checking on userdebug builds\n"
" keygen FILE\n"
" generate adb public/private key; private key stored in FILE,\n"
- " public key stored in FILE.pub (existing files overwritten)\n"
"\n"
"scripting:\n"
" wait-for[-TRANSPORT]-STATE\n"
" wait for device to be in the given state\n"
- " State: device, recovery, sideload, or bootloader\n"
- " Transport: usb, local, or any [default=any]\n"
+ " STATE: device, recovery, rescue, sideload, bootloader, or disconnect\n"
+ " TRANSPORT: usb, local, or any [default=any]\n"
" get-state print offline | bootloader | device\n"
" get-serialno print <serial-number>\n"
" get-devpath print <device-path>\n"
- " remount remount partitions read-write\n"
+ " remount [-R]\n"
+ " remount partitions read-write. if a reboot is required, -R will\n"
+ " will automatically reboot the device.\n"
" reboot [bootloader|recovery|sideload|sideload-auto-reboot]\n"
" reboot the device; defaults to booting system image but\n"
" supports bootloader and recovery too. sideload reboots\n"
@@ -220,7 +218,9 @@
" all,adb,sockets,packets,rwx,usb,sync,sysdeps,transport,jdwp\n"
" $ADB_VENDOR_KEYS colon-separated list of keys (files or directories)\n"
" $ANDROID_SERIAL serial number to connect to (see -s)\n"
- " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n");
+ " $ANDROID_LOG_TAGS tags to be used by logcat (see logcat --help)\n"
+ " $ADB_LOCAL_TRANSPORT_MAX_PORT max emulator scan port (default 5585, 16 emus)\n"
+ );
// clang-format on
}
@@ -258,7 +258,7 @@
// stdout/stderr are routed independently and the remote exit code will be
// returned.
// if |callback| is non-null, stdout/stderr output will be handled by it.
-int read_and_dump(int fd, bool use_shell_protocol = false,
+int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
int exit_code = 0;
if (fd < 0) return exit_code;
@@ -291,16 +291,19 @@
callback->OnStderr(buffer_ptr, length);
break;
case ShellProtocol::kIdExit:
- exit_code = protocol->data()[0];
+ // data() returns a char* which doesn't have defined signedness.
+ // Cast to uint8_t to prevent 255 from being sign extended to INT_MIN,
+ // which doesn't get truncated on Windows.
+ exit_code = static_cast<uint8_t>(protocol->data()[0]);
continue;
default:
continue;
}
length = protocol->data_length();
} else {
- D("read_and_dump(): pre adb_read(fd=%d)", fd);
+ D("read_and_dump(): pre adb_read(fd=%d)", fd.get());
length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
- D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+ D("read_and_dump(): post adb_read(fd=%d): length=%d", fd.get(), length);
if (length <= 0) {
break;
}
@@ -582,12 +585,8 @@
//
// On success returns the remote exit code if |use_shell_protocol| is true,
// 0 otherwise. On failure returns 1.
-static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
- char escape_char,
- const std::string& command) {
- std::string service_string = ShellServiceString(use_shell_protocol,
- type_arg, command);
-
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, char escape_char,
+ bool empty_command, const std::string& service_string) {
// Old devices can't handle a service string that's longer than MAX_PAYLOAD_V1.
// Use |use_shell_protocol| to determine whether to allow a command longer than that.
if (service_string.size() > MAX_PAYLOAD_V1 && !use_shell_protocol) {
@@ -598,8 +597,7 @@
// Make local stdin raw if the device allocates a PTY, which happens if:
// 1. We are explicitly asking for a PTY shell, or
// 2. We don't specify shell type and are starting an interactive session.
- bool raw_stdin = (type_arg == kShellServiceArgPty ||
- (type_arg.empty() && command.empty()));
+ bool raw_stdin = (type_arg == kShellServiceArgPty || (type_arg.empty() && empty_command));
std::string error;
int fd = adb_connect(service_string, &error);
@@ -759,12 +757,44 @@
command = android::base::Join(std::vector<const char*>(argv + optind, argv + argc), ' ');
}
- return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
+ std::string service_string = ShellServiceString(use_shell_protocol, shell_type_arg, command);
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command.empty(),
+ service_string);
+}
+
+static int adb_abb(int argc, const char** argv) {
+ FeatureSet features;
+ std::string error_message;
+ if (!adb_get_feature_set(&features, &error_message)) {
+ fprintf(stderr, "error: %s\n", error_message.c_str());
+ return 1;
+ }
+
+ if (!CanUseFeature(features, kFeatureAbb)) {
+ error_exit("abb is not supported by the device");
+ }
+
+ optind = 1; // argv[0] is always "abb", so set `optind` appropriately.
+
+ // Defaults.
+ constexpr char escape_char = '~'; // -e
+ constexpr bool use_shell_protocol = true;
+ constexpr auto shell_type_arg = kShellServiceArgRaw;
+ constexpr bool empty_command = false;
+
+ std::vector<const char*> args(argv + optind, argv + argc);
+ std::string service_string = "abb:" + android::base::Join(args, ABB_ARG_DELIMETER);
+
+ D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
+ service_string.data());
+
+ return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, empty_command,
+ service_string);
}
static int adb_sideload_legacy(const char* filename, int in_fd, int size) {
std::string error;
- int out_fd = adb_connect(android::base::StringPrintf("sideload:%d", size), &error);
+ unique_fd out_fd(adb_connect(android::base::StringPrintf("sideload:%d", size), &error));
if (out_fd < 0) {
fprintf(stderr, "adb: pre-KitKat sideload connection failed: %s\n", error.c_str());
return -1;
@@ -779,14 +809,12 @@
unsigned xfer = (size > CHUNK_SIZE) ? CHUNK_SIZE : size;
if (!ReadFdExactly(in_fd, buf, xfer)) {
fprintf(stderr, "adb: failed to read data from %s: %s\n", filename, strerror(errno));
- adb_close(out_fd);
return -1;
}
if (!WriteFdExactly(out_fd, buf, xfer)) {
std::string error;
adb_status(out_fd, &error);
fprintf(stderr, "adb: failed to write data: %s\n", error.c_str());
- adb_close(out_fd);
return -1;
}
size -= xfer;
@@ -797,36 +825,33 @@
if (!adb_status(out_fd, &error)) {
fprintf(stderr, "adb: error response: %s\n", error.c_str());
- adb_close(out_fd);
return -1;
}
- adb_close(out_fd);
return 0;
}
#define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE)
-/*
- * The sideload-host protocol serves the data in a file (given on the
- * command line) to the client, using a simple protocol:
- *
- * - The connect message includes the total number of bytes in the
- * file and a block size chosen by us.
- *
- * - The other side sends the desired block number as eight decimal
- * digits (eg "00000023" for block 23). Blocks are numbered from
- * zero.
- *
- * - We send back the data of the requested block. The last block is
- * likely to be partial; when the last block is requested we only
- * send the part of the block that exists, it's not padded up to the
- * block size.
- *
- * - When the other side sends "DONEDONE" instead of a block number,
- * we hang up.
- */
-static int adb_sideload_host(const char* filename) {
+// Connects to the sideload / rescue service on the device (served by minadbd) and sends over the
+// data in an OTA package.
+//
+// It uses a simple protocol as follows.
+//
+// - The connect message includes the total number of bytes in the file and a block size chosen by
+// us.
+//
+// - The other side sends the desired block number as eight decimal digits (e.g. "00000023" for
+// block 23). Blocks are numbered from zero.
+//
+// - We send back the data of the requested block. The last block is likely to be partial; when the
+// last block is requested we only send the part of the block that exists, it's not padded up to
+// the block size.
+//
+// - When the other side sends "DONEDONE" or "FAILFAIL" instead of a block number, we have done all
+// the data transfer.
+//
+static int adb_sideload_install(const char* filename, bool rescue_mode) {
// TODO: use a LinePrinter instead...
struct stat sb;
if (stat(filename, &sb) == -1) {
@@ -839,21 +864,25 @@
return -1;
}
- std::string service =
- android::base::StringPrintf("sideload-host:%" PRId64 ":%d",
- static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
+ std::string service = android::base::StringPrintf(
+ "%s:%" PRId64 ":%d", rescue_mode ? "rescue-install" : "sideload-host",
+ static_cast<int64_t>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
unique_fd device_fd(adb_connect(service, &error));
if (device_fd < 0) {
fprintf(stderr, "adb: sideload connection failed: %s\n", error.c_str());
+ if (rescue_mode) {
+ return -1;
+ }
+
// If this is a small enough package, maybe this is an older device that doesn't
// support sideload-host. Try falling back to the older (<= K) sideload method.
if (sb.st_size > INT_MAX) {
return -1;
}
fprintf(stderr, "adb: trying pre-KitKat sideload method...\n");
- return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
+ return adb_sideload_legacy(filename, package_fd.get(), static_cast<int>(sb.st_size));
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -870,10 +899,14 @@
}
buf[8] = '\0';
- if (strcmp("DONEDONE", buf) == 0) {
+ if (strcmp(kMinadbdServicesExitSuccess, buf) == 0 ||
+ strcmp(kMinadbdServicesExitFailure, buf) == 0) {
printf("\rTotal xfer: %.2fx%*s\n",
static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
static_cast<int>(strlen(filename) + 10), "");
+ if (strcmp(kMinadbdServicesExitFailure, buf) == 0) {
+ return 1;
+ }
return 0;
}
@@ -923,6 +956,33 @@
}
}
+static int adb_wipe_devices() {
+ auto wipe_devices_message_size = strlen(kMinadbdServicesExitSuccess);
+ std::string error;
+ unique_fd fd(adb_connect(
+ android::base::StringPrintf("rescue-wipe:userdata:%zu", wipe_devices_message_size),
+ &error));
+ if (fd < 0) {
+ fprintf(stderr, "adb: wipe device connection failed: %s\n", error.c_str());
+ return 1;
+ }
+
+ std::string message(wipe_devices_message_size, '\0');
+ if (!ReadFdExactly(fd, message.data(), wipe_devices_message_size)) {
+ fprintf(stderr, "adb: failed to read wipe result: %s\n", strerror(errno));
+ return 1;
+ }
+
+ if (message == kMinadbdServicesExitSuccess) {
+ return 0;
+ }
+
+ if (message != kMinadbdServicesExitFailure) {
+ fprintf(stderr, "adb: got unexpected message from rescue wipe %s\n", message.c_str());
+ }
+ return 1;
+}
+
/**
* Run ppp in "notty" mode against a resource listed as the first parameter
* eg:
@@ -977,7 +1037,8 @@
#endif /* !defined(_WIN32) */
}
-static bool wait_for_device(const char* service) {
+static bool wait_for_device(const char* service,
+ std::optional<std::chrono::milliseconds> timeout = std::nullopt) {
std::vector<std::string> components = android::base::Split(service, "-");
if (components.size() < 3 || components.size() > 4) {
fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
@@ -1005,22 +1066,32 @@
}
if (components[3] != "any" && components[3] != "bootloader" && components[3] != "device" &&
- components[3] != "recovery" && components[3] != "sideload") {
+ components[3] != "recovery" && components[3] != "rescue" && components[3] != "sideload" &&
+ components[3] != "disconnect") {
fprintf(stderr,
"adb: unknown state %s; "
- "expected 'any', 'bootloader', 'device', 'recovery', or 'sideload'\n",
+ "expected 'any', 'bootloader', 'device', 'recovery', 'rescue', 'sideload', or "
+ "'disconnect'\n",
components[3].c_str());
return false;
}
std::string cmd = format_host_command(android::base::Join(components, "-").c_str());
+ if (timeout) {
+ std::thread([timeout]() {
+ std::this_thread::sleep_for(*timeout);
+ fprintf(stderr, "timeout expired while waiting for device\n");
+ _exit(1);
+ }).detach();
+ }
return adb_command(cmd);
}
static bool adb_root(const char* command) {
std::string error;
- unique_fd fd(adb_connect(android::base::StringPrintf("%s:", command), &error));
+ TransportId transport_id;
+ unique_fd fd(adb_connect(&transport_id, android::base::StringPrintf("%s:", command), &error));
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
return false;
@@ -1053,15 +1124,28 @@
return true;
}
- // Give adbd some time to kill itself and come back up.
- // We can't use wait-for-device because devices (e.g. adb over network) might not come back.
- std::this_thread::sleep_for(3s);
+ // Wait for the device to go away.
+ TransportType previous_type;
+ const char* previous_serial;
+ TransportId previous_id;
+ adb_get_transport(&previous_type, &previous_serial, &previous_id);
+
+ adb_set_transport(kTransportAny, nullptr, transport_id);
+ wait_for_device("wait-for-disconnect");
+
+ // Wait for the device to come back.
+ // If we were using a specific transport ID, there's nothing we can wait for.
+ if (previous_id == 0) {
+ adb_set_transport(previous_type, previous_serial, 0);
+ wait_for_device("wait-for-device", 6000ms);
+ }
+
return true;
}
int send_shell_command(const std::string& command, bool disable_shell_protocol,
StandardStreamsCallbackInterface* callback) {
- int fd;
+ unique_fd fd;
bool use_shell_protocol = false;
while (true) {
@@ -1084,7 +1168,7 @@
std::string error;
std::string service_string = ShellServiceString(use_shell_protocol, "", command);
- fd = adb_connect(service_string, &error);
+ fd.reset(adb_connect(service_string, &error));
if (fd >= 0) {
break;
}
@@ -1096,13 +1180,7 @@
}
}
- int exit_code = read_and_dump(fd, use_shell_protocol, callback);
-
- if (adb_close(fd) < 0) {
- PLOG(ERROR) << "failure closing FD " << fd;
- }
-
- return exit_code;
+ return read_and_dump(fd.get(), use_shell_protocol, callback);
}
static int logcat(int argc, const char** argv) {
@@ -1124,14 +1202,14 @@
return send_shell_command(cmd);
}
-static void write_zeros(int bytes, int fd) {
+static void write_zeros(int bytes, borrowed_fd fd) {
int old_stdin_mode = -1;
int old_stdout_mode = -1;
std::vector<char> buf(bytes);
- D("write_zeros(%d) -> %d", bytes, fd);
+ D("write_zeros(%d) -> %d", bytes, fd.get());
- stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+ stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
if (fd == STDOUT_FILENO) {
fwrite(buf.data(), 1, bytes, stdout);
@@ -1140,12 +1218,14 @@
adb_write(fd, buf.data(), bytes);
}
- stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+ stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
D("write_zeros() finished");
}
static int backup(int argc, const char** argv) {
+ fprintf(stdout, "WARNING: adb backup is deprecated and may be removed in a future release\n");
+
const char* filename = "backup.ab";
/* find, extract, and use any -f argument */
@@ -1166,7 +1246,7 @@
if (argc < 2) error_exit("backup either needs a list of packages or -all/-shared");
adb_unlink(filename);
- int outFd = adb_creat(filename, 0640);
+ unique_fd outFd(adb_creat(filename, 0640));
if (outFd < 0) {
fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
return EXIT_FAILURE;
@@ -1181,54 +1261,48 @@
D("backup. filename=%s cmd=%s", filename, cmd.c_str());
std::string error;
- int fd = adb_connect(cmd, &error);
+ unique_fd fd(adb_connect(cmd, &error));
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
- adb_close(outFd);
return EXIT_FAILURE;
}
fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
fflush(stdout);
- copy_to_file(fd, outFd);
-
- adb_close(fd);
- adb_close(outFd);
+ copy_to_file(fd.get(), outFd.get());
return EXIT_SUCCESS;
}
static int restore(int argc, const char** argv) {
+ fprintf(stdout, "WARNING: adb restore is deprecated and may be removed in a future release\n");
+
if (argc != 2) error_exit("restore requires an argument");
const char* filename = argv[1];
- int tarFd = adb_open(filename, O_RDONLY);
+ unique_fd tarFd(adb_open(filename, O_RDONLY));
if (tarFd < 0) {
fprintf(stderr, "adb: unable to open file %s: %s\n", filename, strerror(errno));
return -1;
}
std::string error;
- int fd = adb_connect("restore:", &error);
+ unique_fd fd(adb_connect("restore:", &error));
if (fd < 0) {
fprintf(stderr, "adb: unable to connect for restore: %s\n", error.c_str());
- adb_close(tarFd);
return -1;
}
fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
fflush(stdout);
- copy_to_file(tarFd, fd);
+ copy_to_file(tarFd.get(), fd.get());
// Provide an in-band EOD marker in case the archive file is malformed
- write_zeros(512*2, fd);
+ write_zeros(512 * 2, fd);
// Wait until the other side finishes, or it'll get sent SIGHUP.
- copy_to_file(fd, STDOUT_FILENO);
-
- adb_close(fd);
- adb_close(tarFd);
+ copy_to_file(fd.get(), STDOUT_FILENO);
return 0;
}
@@ -1266,15 +1340,49 @@
}
}
-static int adb_connect_command(const std::string& command) {
+static int adb_connect_command(const std::string& command, TransportId* transport = nullptr) {
std::string error;
- int fd = adb_connect(command, &error);
+ unique_fd fd(adb_connect(transport, command, &error));
if (fd < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
return 1;
}
read_and_dump(fd);
- adb_close(fd);
+ return 0;
+}
+
+static int adb_connect_command_bidirectional(const std::string& command) {
+ std::string error;
+ unique_fd fd(adb_connect(command, &error));
+ if (fd < 0) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return 1;
+ }
+
+ static constexpr auto forward = [](int src, int sink, bool exit_on_end) {
+ char buf[4096];
+ while (true) {
+ int rc = adb_read(src, buf, sizeof(buf));
+ if (rc == 0) {
+ if (exit_on_end) {
+ exit(0);
+ } else {
+ adb_shutdown(sink, SHUT_WR);
+ }
+ return;
+ } else if (rc < 0) {
+ perror_exit("read failed");
+ }
+ if (!WriteFdExactly(sink, buf, rc)) {
+ perror_exit("write failed");
+ }
+ }
+ };
+
+ std::thread read(forward, fd.get(), STDOUT_FILENO, true);
+ std::thread write(forward, STDIN_FILENO, fd.get(), false);
+ read.join();
+ write.join();
return 0;
}
@@ -1323,9 +1431,9 @@
TransportId transport_id = 0;
while (argc > 0) {
- if (!strcmp(argv[0],"server")) {
+ if (!strcmp(argv[0], "server")) {
is_server = true;
- } else if (!strcmp(argv[0],"nodaemon")) {
+ } else if (!strcmp(argv[0], "nodaemon")) {
no_daemon = true;
} else if (!strcmp(argv[0], "fork-server")) {
/* this is a special flag used only when the ADB client launches the ADB Server */
@@ -1362,11 +1470,11 @@
if (*id != '\0') {
error_exit("invalid transport id");
}
- } else if (!strcmp(argv[0],"-d")) {
+ } else if (!strcmp(argv[0], "-d")) {
transport_type = kTransportUsb;
- } else if (!strcmp(argv[0],"-e")) {
+ } else if (!strcmp(argv[0], "-e")) {
transport_type = kTransportLocal;
- } else if (!strcmp(argv[0],"-a")) {
+ } else if (!strcmp(argv[0], "-a")) {
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
@@ -1498,29 +1606,32 @@
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
+ std::string error;
+ if (!adb_check_server_version(&error)) {
+ error_exit("failed to check server version: %s", error.c_str());
+ }
printf("List of devices attached\n");
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
+ if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
+ if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
return adb_query_command(query);
- }
- else if (!strcmp(argv[0], "emu")) {
+ } else if (!strcmp(argv[0], "abb")) {
+ return adb_abb(argc, argv);
+ } else if (!strcmp(argv[0], "emu")) {
return adb_send_emulator_command(argc, argv, serial);
- }
- else if (!strcmp(argv[0], "shell")) {
+ } else if (!strcmp(argv[0], "shell")) {
return adb_shell(argc, argv);
- }
- else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
+ } else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
if (argc < 2) error_exit("usage: adb %s command", argv[0]);
@@ -1534,31 +1645,56 @@
}
std::string error;
- int fd = adb_connect(cmd, &error);
+ unique_fd fd(adb_connect(cmd, &error));
if (fd < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
return -1;
}
if (exec_in) {
- copy_to_file(STDIN_FILENO, fd);
+ copy_to_file(STDIN_FILENO, fd.get());
} else {
- copy_to_file(fd, STDOUT_FILENO);
+ copy_to_file(fd.get(), STDOUT_FILENO);
}
-
- adb_close(fd);
return 0;
- }
- else if (!strcmp(argv[0], "kill-server")) {
+ } else if (!strcmp(argv[0], "kill-server")) {
return adb_kill_server() ? 0 : 1;
- }
- else if (!strcmp(argv[0], "sideload")) {
+ } else if (!strcmp(argv[0], "sideload")) {
if (argc != 2) error_exit("sideload requires an argument");
- if (adb_sideload_host(argv[1])) {
+ if (adb_sideload_install(argv[1], false /* rescue_mode */)) {
return 1;
} else {
return 0;
}
+ } else if (!strcmp(argv[0], "rescue")) {
+ // adb rescue getprop
+ // adb rescue getprop <prop>
+ // adb rescue install <filename>
+ // adb rescue wipe userdata
+ if (argc < 2) error_exit("rescue requires at least one argument");
+ if (!strcmp(argv[1], "getprop")) {
+ if (argc == 2) {
+ return adb_connect_command("rescue-getprop:");
+ }
+ if (argc == 3) {
+ return adb_connect_command(
+ android::base::StringPrintf("rescue-getprop:%s", argv[2]));
+ }
+ error_exit("invalid rescue getprop arguments");
+ } else if (!strcmp(argv[1], "install")) {
+ if (argc != 3) error_exit("rescue install requires two arguments");
+ if (adb_sideload_install(argv[2], true /* rescue_mode */) != 0) {
+ return 1;
+ }
+ } else if (!strcmp(argv[1], "wipe")) {
+ if (argc != 3 || strcmp(argv[2], "userdata") != 0) {
+ error_exit("invalid rescue wipe arguments");
+ }
+ return adb_wipe_devices();
+ } else {
+ error_exit("invalid rescue argument");
+ }
+ return 0;
} else if (!strcmp(argv[0], "tcpip")) {
if (argc != 2) error_exit("tcpip requires an argument");
int port;
@@ -1643,9 +1779,8 @@
error_exit("error: %s", error_message.c_str());
}
- int fd = adb_connect(cmd, &error_message);
- if (fd < 0 || !adb_status(fd, &error_message)) {
- adb_close(fd);
+ unique_fd fd(adb_connect(cmd, &error_message));
+ if (fd < 0 || !adb_status(fd.get(), &error_message)) {
error_exit("error: %s", error_message.c_str());
}
@@ -1662,8 +1797,7 @@
else if (!strcmp(argv[0], "ls")) {
if (argc != 2) error_exit("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "push")) {
+ } else if (!strcmp(argv[0], "push")) {
bool copy_attrs = false;
bool sync = false;
std::vector<const char*> srcs;
@@ -1672,8 +1806,7 @@
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, &sync);
if (srcs.empty() || !dst) error_exit("push requires an argument");
return do_sync_push(srcs, dst, sync) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "pull")) {
+ } else if (!strcmp(argv[0], "pull")) {
bool copy_attrs = false;
std::vector<const char*> srcs;
const char* dst = ".";
@@ -1681,20 +1814,19 @@
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs, nullptr);
if (srcs.empty()) error_exit("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
- }
- else if (!strcmp(argv[0], "install")) {
+ } else if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
- }
- else if (!strcmp(argv[0], "install-multiple")) {
+ } else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) error_exit("install-multiple requires an argument");
return install_multiple_app(argc, argv);
- }
- else if (!strcmp(argv[0], "uninstall")) {
+ } else if (!strcmp(argv[0], "install-multi-package")) {
+ if (argc < 2) error_exit("install-multi-package requires an argument");
+ return install_multi_package(argc, argv);
+ } else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) error_exit("uninstall requires an argument");
return uninstall_app(argc, argv);
- }
- else if (!strcmp(argv[0], "sync")) {
+ } else if (!strcmp(argv[0], "sync")) {
std::string src;
bool list_only = false;
if (argc < 2) {
@@ -1709,8 +1841,8 @@
}
if (src.empty()) src = "all";
- std::vector<std::string> partitions{"data", "odm", "oem", "product", "product_services",
- "system", "vendor"};
+ std::vector<std::string> partitions{"data", "odm", "oem", "product",
+ "system", "system_ext", "vendor"};
bool found = false;
for (const auto& partition : partitions) {
if (src == "all" || src == partition) {
@@ -1724,61 +1856,56 @@
return 0;
}
/* passthrough commands */
- else if (!strcmp(argv[0],"get-state") ||
- !strcmp(argv[0],"get-serialno") ||
- !strcmp(argv[0],"get-devpath"))
- {
+ else if (!strcmp(argv[0], "get-state") || !strcmp(argv[0], "get-serialno") ||
+ !strcmp(argv[0], "get-devpath")) {
return adb_query_command(format_host_command(argv[0]));
}
/* other commands */
- else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
+ else if (!strcmp(argv[0], "logcat") || !strcmp(argv[0], "lolcat") ||
+ !strcmp(argv[0], "longcat")) {
return logcat(argc, argv);
- }
- else if (!strcmp(argv[0],"ppp")) {
+ } else if (!strcmp(argv[0], "ppp")) {
return ppp(argc, argv);
- }
- else if (!strcmp(argv[0], "start-server")) {
+ } else if (!strcmp(argv[0], "start-server")) {
std::string error;
const int result = adb_connect("host:start-server", &error);
if (result < 0) {
fprintf(stderr, "error: %s\n", error.c_str());
}
return result;
- }
- else if (!strcmp(argv[0], "backup")) {
+ } else if (!strcmp(argv[0], "backup")) {
return backup(argc, argv);
- }
- else if (!strcmp(argv[0], "restore")) {
+ } else if (!strcmp(argv[0], "restore")) {
return restore(argc, argv);
- }
- else if (!strcmp(argv[0], "keygen")) {
+ } else if (!strcmp(argv[0], "keygen")) {
if (argc != 2) error_exit("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
- }
- else if (!strcmp(argv[0], "jdwp")) {
+ } else if (!strcmp(argv[0], "pubkey")) {
+ if (argc != 2) error_exit("pubkey requires an argument");
+ return adb_auth_pubkey(argv[1]);
+ } else if (!strcmp(argv[0], "jdwp")) {
return adb_connect_command("jdwp");
- }
- else if (!strcmp(argv[0], "track-jdwp")) {
+ } else if (!strcmp(argv[0], "track-jdwp")) {
return adb_connect_command("track-jdwp");
- }
- else if (!strcmp(argv[0], "track-devices")) {
- return adb_connect_command("host:track-devices");
+ } else if (!strcmp(argv[0], "track-devices")) {
+ if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) {
+ error_exit("usage: adb track-devices [-l]");
+ }
+ return adb_connect_command(argc == 2 ? "host:track-devices-l" : "host:track-devices");
} else if (!strcmp(argv[0], "raw")) {
if (argc != 2) {
error_exit("usage: adb raw SERVICE");
}
- return adb_connect_command(argv[1]);
+ return adb_connect_command_bidirectional(argv[1]);
}
-
/* "adb /?" is a common idiom under Windows */
else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
help();
return 0;
- }
- else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
+ } else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
fprintf(stdout, "%s", adb_version().c_str());
return 0;
} else if (!strcmp(argv[0], "features")) {
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index 4e8a3f8..d10f4de 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -71,7 +71,7 @@
return -1;
}
- int port;
+ int port = -1;
size_t emulator_count = 0;
for (const auto& device : android::base::Split(devices, "\n")) {
if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
@@ -108,7 +108,7 @@
}
int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
- int fd = connect_to_console(serial);
+ unique_fd fd(connect_to_console(serial));
if (fd == -1) {
return 1;
}
@@ -125,7 +125,6 @@
if (!WriteFdExactly(fd, commands)) {
fprintf(stderr, "error: cannot write to emulator: %s\n",
strerror(errno));
- adb_close(fd);
return 1;
}
@@ -178,7 +177,5 @@
}
printf("%s", emulator_output.c_str() + found);
- adb_close(fd);
-
return 0;
}
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 45f3cca..fbae219 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -16,6 +16,7 @@
#include "fastdeploy.h"
+#include <string.h>
#include <algorithm>
#include <array>
#include <memory>
@@ -26,14 +27,19 @@
#include "androidfw/ZipFileRO.h"
#include "client/file_sync_client.h"
#include "commandline.h"
+#include "deployagent.inc" // Generated include via build rule.
+#include "deployagentscript.inc" // Generated include via build rule.
+#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
#include "fastdeploycallbacks.h"
#include "sysdeps.h"
#include "adb_utils.h"
-static constexpr long kRequiredAgentVersion = 0x00000001;
+static constexpr long kRequiredAgentVersion = 0x00000002;
static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
+static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
static bool g_use_localagent = false;
@@ -70,46 +76,32 @@
g_use_localagent = use_localagent;
}
-// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
-static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
- std::string adb_dir = android::base::GetExecutableDirectory();
- if (adb_dir.empty()) {
- error_exit("Could not determine location of adb!");
- }
-
- if (g_use_localagent) {
- const char* product_out = getenv("ANDROID_PRODUCT_OUT");
- if (product_out == nullptr) {
- error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
- local_path);
- }
- return android::base::StringPrintf("%s%s", product_out, local_path);
- } else {
- return adb_dir + sdk_path;
- }
-}
-
static bool deploy_agent(bool checkTimeStamps) {
std::vector<const char*> srcs;
- std::string jar_path =
- get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar");
- std::string script_path =
- get_agent_component_host_path("/system/bin/deployagent", "/deployagent");
- srcs.push_back(jar_path.c_str());
- srcs.push_back(script_path.c_str());
-
- if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
- // on windows the shell script might have lost execute permission
- // so need to set this explicitly
- const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
- std::string chmodCommand =
- android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
- int ret = send_shell_command(chmodCommand);
- if (ret != 0) {
- error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
- }
- } else {
- error_exit("Error pushing agent files to device");
+ // TODO: Deploy agent from bin2c directly instead of writing to disk first.
+ TemporaryFile tempAgent;
+ android::base::WriteFully(tempAgent.fd, kDeployAgent, sizeof(kDeployAgent));
+ srcs.push_back(tempAgent.path);
+ if (!do_sync_push(srcs, kDeviceAgentFile, checkTimeStamps)) {
+ error_exit("Failed to push fastdeploy agent to device.");
+ }
+ srcs.clear();
+ // TODO: Deploy agent from bin2c directly instead of writing to disk first.
+ TemporaryFile tempAgentScript;
+ android::base::WriteFully(tempAgentScript.fd, kDeployAgentScript, sizeof(kDeployAgentScript));
+ srcs.push_back(tempAgentScript.path);
+ if (!do_sync_push(srcs, kDeviceAgentScript, checkTimeStamps)) {
+ error_exit("Failed to push fastdeploy agent script to device.");
+ }
+ srcs.clear();
+ // on windows the shell script might have lost execute permission
+ // so need to set this explicitly
+ const char* kChmodCommandPattern = "chmod 777 %s";
+ std::string chmodCommand =
+ android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
+ int ret = send_shell_command(chmodCommand);
+ if (ret != 0) {
+ error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
}
return true;
@@ -228,42 +220,24 @@
android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
std::vector<char> extractErrorBuffer;
- int statusCode;
- DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
+ DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
int returnCode = send_shell_command(extractCommand, false, &cb);
if (returnCode != 0) {
- error_exit("Executing %s returned %d", extractCommand.c_str(), returnCode);
+ fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
+ fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
+ error_exit("Aborting");
}
}
-static std::string get_patch_generator_command() {
- if (g_use_localagent) {
- // This should never happen on a Windows machine
- const char* host_out = getenv("ANDROID_HOST_OUT");
- if (host_out == nullptr) {
- error_exit(
- "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
- "is not defined");
- }
- return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
- host_out);
- }
-
- std::string adb_dir = android::base::GetExecutableDirectory();
- if (adb_dir.empty()) {
- error_exit("Could not locate deploypatchgenerator.jar");
- }
- return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
- adb_dir.c_str());
-}
-
void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
- std::string generatePatchCommand = android::base::StringPrintf(
- R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
- patchPath);
- int returnCode = system(generatePatchCommand.c_str());
- if (returnCode != 0) {
- error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+ DeployPatchGenerator generator(false);
+ unique_fd patchFd(adb_open(patchPath, O_WRONLY | O_CREAT | O_CLOEXEC));
+ if (patchFd < 0) {
+ perror_exit("adb: failed to create %s", patchPath);
+ }
+ bool success = generator.CreatePatch(apkPath, metadataPath, patchFd);
+ if (!success) {
+ error_exit("Failed to create patch for %s", apkPath);
}
}
@@ -312,9 +286,16 @@
std::vector<unsigned char> applyErrorBuffer;
std::string argsString;
+ bool rSwitchPresent = false;
for (int i = 0; i < argc; i++) {
argsString.append(argv[i]);
argsString.append(" ");
+ if (!strcmp(argv[i], "-r")) {
+ rSwitchPresent = true;
+ }
+ }
+ if (!rSwitchPresent) {
+ argsString.append("-r");
}
std::string applyPatchCommand =
@@ -325,3 +306,9 @@
error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
}
}
+
+bool find_package(const char* apkPath) {
+ const std::string findCommand =
+ "/data/local/tmp/deployagent find " + get_packagename_from_apk(apkPath);
+ return !send_shell_command(findCommand);
+}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index a6b10d3..7b7f2ec 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -32,3 +32,4 @@
void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
std::string get_patch_path(const char* apkPath);
+bool find_package(const char* apkPath);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
index 6c9a21f..23a0aca 100644
--- a/adb/client/fastdeploycallbacks.cpp
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -35,8 +35,7 @@
class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
public:
- DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer,
- int* statusCode);
+ DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer);
virtual void OnStdout(const char* buffer, int length);
virtual void OnStderr(const char* buffer, int length);
@@ -45,27 +44,17 @@
private:
std::vector<char>* mpOutBuffer;
std::vector<char>* mpErrBuffer;
- int* mpStatusCode;
};
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
std::vector<char>* errBuffer) {
- int statusCode;
- DeployAgentBufferCallback cb(outBuffer, errBuffer, &statusCode);
- int ret = send_shell_command(command, false, &cb);
-
- if (ret == 0) {
- return statusCode;
- } else {
- return ret;
- }
+ DeployAgentBufferCallback cb(outBuffer, errBuffer);
+ return send_shell_command(command, false, &cb);
}
-DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer,
- int* statusCode) {
+DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer) {
mpOutFile = outputFile;
mpErrBuffer = errBuffer;
- mpStatusCode = statusCode;
mBytesWritten = 0;
}
@@ -84,10 +73,7 @@
}
int DeployAgentFileCallback::Done(int status) {
- if (mpStatusCode != NULL) {
- *mpStatusCode = status;
- }
- return 0;
+ return status;
}
int DeployAgentFileCallback::getBytesWritten() {
@@ -95,11 +81,9 @@
}
DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
- std::vector<char>* errBuffer,
- int* statusCode) {
+ std::vector<char>* errBuffer) {
mpOutBuffer = outBuffer;
mpErrBuffer = errBuffer;
- mpStatusCode = statusCode;
}
void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
@@ -111,8 +95,5 @@
}
int DeployAgentBufferCallback::Done(int status) {
- if (mpStatusCode != NULL) {
- *mpStatusCode = status;
- }
- return 0;
+ return status;
}
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
index b428b50..7e049c5 100644
--- a/adb/client/fastdeploycallbacks.h
+++ b/adb/client/fastdeploycallbacks.h
@@ -21,7 +21,7 @@
class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
public:
- DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer, int* statusCode);
+ DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer);
virtual void OnStdout(const char* buffer, int length);
virtual void OnStderr(const char* buffer, int length);
@@ -33,7 +33,6 @@
FILE* mpOutFile;
std::vector<char>* mpErrBuffer;
int mBytesWritten;
- int* mpStatusCode;
};
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index f0f9a80..5d10238 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -207,11 +207,10 @@
std::string error;
if (!adb_get_feature_set(&features_, &error)) {
- fd = -1;
Error("failed to get feature set: %s", error.c_str());
} else {
have_stat_v2_ = CanUseFeature(features_, kFeatureStat2);
- fd = adb_connect("sync:", &error);
+ fd.reset(adb_connect("sync:", &error));
if (fd < 0) {
Error("connect failed: %s", error.c_str());
}
@@ -230,7 +229,6 @@
// case, this will wait for the server to do orderly shutdown.
ReadOrderlyShutdown(fd);
}
- adb_close(fd);
line_printer_.KeepInfoLine();
}
@@ -240,7 +238,7 @@
bool IsValid() { return fd >= 0; }
bool ReceivedError(const char* from, const char* to) {
- adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
int rc = adb_poll(&pfd, 1, 0);
if (rc < 0) {
Error("failed to poll: %s", strerror(errno));
@@ -324,7 +322,7 @@
memset(st, 0, sizeof(*st));
if (have_stat_v2_) {
- if (!ReadFdExactly(fd, &msg.stat_v2, sizeof(msg.stat_v2))) {
+ if (!ReadFdExactly(fd.get(), &msg.stat_v2, sizeof(msg.stat_v2))) {
PLOG(FATAL) << "protocol fault: failed to read stat response";
}
@@ -350,7 +348,7 @@
st->st_ctime = msg.stat_v2.ctime;
return true;
} else {
- if (!ReadFdExactly(fd, &msg.stat_v1, sizeof(msg.stat_v1))) {
+ if (!ReadFdExactly(fd.get(), &msg.stat_v1, sizeof(msg.stat_v1))) {
PLOG(FATAL) << "protocol fault: failed to read stat response";
}
@@ -437,7 +435,7 @@
uint64_t total_size = st.st_size;
uint64_t bytes_copied = 0;
- int lfd = adb_open(lpath, O_RDONLY);
+ unique_fd lfd(adb_open(lpath, O_RDONLY));
if (lfd < 0) {
Error("opening '%s' locally failed: %s", lpath, strerror(errno));
return false;
@@ -449,7 +447,6 @@
int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest));
if (bytes_read == -1) {
Error("reading '%s' locally failed: %s", lpath, strerror(errno));
- adb_close(lfd);
return false;
} else if (bytes_read == 0) {
break;
@@ -469,8 +466,6 @@
ReportProgress(rpath, bytes_copied, total_size);
}
- adb_close(lfd);
-
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
@@ -576,7 +571,7 @@
}
// TODO: add a char[max] buffer here, to replace syncsendbuf...
- int fd;
+ unique_fd fd;
size_t max;
private:
@@ -686,9 +681,7 @@
if (sync) {
struct stat st;
if (sync_lstat(sc, rpath, &st)) {
- // For links, we cannot update the atime/mtime.
- if ((S_ISREG(mode & st.st_mode) && st.st_mtime == static_cast<time_t>(mtime)) ||
- (S_ISLNK(mode & st.st_mode) && st.st_mtime >= static_cast<time_t>(mtime))) {
+ if (st.st_mtime == static_cast<time_t>(mtime)) {
sc.RecordFilesSkipped(1);
return true;
}
@@ -740,7 +733,7 @@
if (!sc.SendRequest(ID_RECV, rpath)) return false;
adb_unlink(lpath);
- int lfd = adb_creat(lpath, 0644);
+ unique_fd lfd(adb_creat(lpath, 0644));
if (lfd < 0) {
sc.Error("cannot create '%s': %s", lpath, strerror(errno));
return false;
@@ -750,7 +743,6 @@
while (true) {
syncmsg msg;
if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- adb_close(lfd);
adb_unlink(lpath);
return false;
}
@@ -758,7 +750,6 @@
if (msg.data.id == ID_DONE) break;
if (msg.data.id != ID_DATA) {
- adb_close(lfd);
adb_unlink(lpath);
sc.ReportCopyFailure(rpath, lpath, msg);
return false;
@@ -766,21 +757,18 @@
if (msg.data.size > sc.max) {
sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
- adb_close(lfd);
adb_unlink(lpath);
return false;
}
char buffer[SYNC_DATA_MAX];
if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
- adb_close(lfd);
adb_unlink(lpath);
return false;
}
if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
sc.Error("cannot write '%s': %s", lpath, strerror(errno));
- adb_close(lfd);
adb_unlink(lpath);
return false;
}
@@ -792,7 +780,6 @@
}
sc.RecordFilesTransferred(1);
- adb_close(lfd);
return true;
}
@@ -932,12 +919,8 @@
for (copyinfo& ci : file_list) {
struct stat st;
if (sc.FinishStat(&st)) {
- if (st.st_size == static_cast<off_t>(ci.size)) {
- // For links, we cannot update the atime/mtime.
- if ((S_ISREG(ci.mode & st.st_mode) && st.st_mtime == ci.time) ||
- (S_ISLNK(ci.mode & st.st_mode) && st.st_mtime >= ci.time)) {
- ci.skip = true;
- }
+ if (st.st_size == static_cast<off_t>(ci.size) && st.st_mtime == ci.time) {
+ ci.skip = true;
}
}
}
@@ -1029,7 +1012,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), sync, false);
+ success &= copy_local_dir_remote(sc, src_path, dst_dir, sync, false);
continue;
} else if (!should_push_file(st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
@@ -1145,7 +1128,7 @@
// Recursively build the list of files to copy.
sc.Printf("pull: building file list...");
std::vector<copyinfo> file_list;
- if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
+ if (!remote_build_list(sc, &file_list, rpath, lpath)) {
return false;
}
@@ -1257,7 +1240,7 @@
dst_dir.append(android::base::Basename(src_path));
}
- success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
+ success &= copy_remote_dir_local(sc, src_path, dst_dir, copy_attrs);
continue;
} else if (!should_pull_file(src_st.st_mode)) {
sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_st.st_mode);
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
index 4dc2d28..50c03e8 100644
--- a/adb/client/line_printer.cpp
+++ b/adb/client/line_printer.cpp
@@ -31,6 +31,8 @@
// Stuff from ninja's util.h that's needed below.
#include <vector>
using namespace std;
+// This does not account for multiple UTF-8 bytes corresponding to a single Unicode code point, or
+// multiple code points corresponding to a single grapheme cluster (user-perceived character).
string ElideMiddle(const string& str, size_t width) {
const int kMargin = 3; // Space for "...".
string result = str;
@@ -85,9 +87,10 @@
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(console_, &csbi);
- // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
- // TODO: wstring ElideMiddle.
to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+ std::wstring to_print_wide;
+ // ElideMiddle may create invalid UTF-8, so ignore conversion errors.
+ (void)android::base::UTF8ToWide(to_print, &to_print_wide);
// We don't want to have the cursor spamming back and forth, so instead of
// printf use WriteConsoleOutput which updates the contents of the buffer,
// but doesn't move the cursor position.
@@ -100,12 +103,10 @@
};
vector<CHAR_INFO> char_data(csbi.dwSize.X);
for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
- // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
- char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
- char_data[i].Attributes = csbi.wAttributes;
+ char_data[i].Char.UnicodeChar = i < to_print_wide.size() ? to_print_wide[i] : L' ';
+ char_data[i].Attributes = csbi.wAttributes;
}
- // TODO: WriteConsoleOutputW.
- WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+ WriteConsoleOutputW(console_, &char_data[0], buf_size, zero_zero, &target);
#else
// Limit output to width of the terminal if provided so we don't cause
// line-wrapping.
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index fb581a6..0c5c28f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -32,15 +32,19 @@
#include "adb.h"
#include "adb_auth.h"
+#include "adb_client.h"
#include "adb_listeners.h"
#include "adb_utils.h"
#include "commandline.h"
#include "sysdeps/chrono.h"
#include "transport.h"
+const char** __adb_argv;
+const char** __adb_envp;
+
static void setup_daemon_logging() {
const std::string log_file_path(GetLogFilePath());
- int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0640);
+ int fd = unix_open(log_file_path, O_WRONLY | O_CREAT | O_APPEND, 0640);
if (fd == -1) {
PLOG(FATAL) << "cannot open " << log_file_path;
}
@@ -191,13 +195,29 @@
notify_thread.detach();
}
+#if defined(__linux__)
+ // Write our location to .android/adb.$PORT, so that older clients can exec us.
+ std::string path;
+ if (!android::base::Readlink("/proc/self/exe", &path)) {
+ PLOG(ERROR) << "failed to readlink /proc/self/exe";
+ }
+
+ std::optional<std::string> server_executable_path = adb_get_server_executable_path();
+ if (server_executable_path) {
+ if (!android::base::WriteStringToFile(path, *server_executable_path)) {
+ PLOG(ERROR) << "failed to write server path to " << path;
+ }
+ }
+#endif
+
D("Event loop starting");
fdevent_loop();
-
return 0;
}
-int main(int argc, char** argv) {
+int main(int argc, char* argv[], char* envp[]) {
+ __adb_argv = const_cast<const char**>(argv);
+ __adb_envp = const_cast<const char**>(envp);
adb_trace_init(argv);
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 283fac5..1a34384 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -31,7 +31,7 @@
#include "adb_mdns.h"
#include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
static DNSServiceRef service_ref;
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index ce57731..f55ae90 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -52,6 +52,11 @@
: native::usb_close(reinterpret_cast<native::usb_handle*>(h));
}
+void usb_reset(usb_handle* h) {
+ should_use_libusb() ? libusb::usb_reset(reinterpret_cast<libusb::usb_handle*>(h))
+ : native::usb_reset(reinterpret_cast<native::usb_handle*>(h));
+}
+
void usb_kick(usb_handle* h) {
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 10b6090..53f01a0 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -332,13 +332,6 @@
return;
}
- rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
- if (rc != 0) {
- LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
- << "'" << libusb_error_name(rc);
- return;
- }
-
for (uint8_t endpoint : {bulk_in, bulk_out}) {
rc = libusb_clear_halt(handle.get(), endpoint);
if (rc != 0) {
@@ -629,6 +622,11 @@
return 0;
}
+void usb_reset(usb_handle* h) {
+ libusb_reset_device(h->device_handle);
+ usb_kick(h);
+}
+
void usb_kick(usb_handle* h) {
h->Close();
}
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index f1bf559..17b4db1 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -39,6 +39,7 @@
#include <list>
#include <mutex>
#include <string>
+#include <string_view>
#include <thread>
#include <android-base/file.h>
@@ -90,7 +91,7 @@
static auto& g_usb_handles_mutex = *new std::mutex();
static auto& g_usb_handles = *new std::list<usb_handle*>();
-static int is_known_device(const char* dev_name) {
+static int is_known_device(std::string_view dev_name) {
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
for (usb_handle* usb : g_usb_handles) {
if (usb->path == dev_name) {
@@ -152,11 +153,11 @@
if (contains_non_digit(de->d_name)) continue;
std::string dev_name = bus_name + "/" + de->d_name;
- if (is_known_device(dev_name.c_str())) {
+ if (is_known_device(dev_name)) {
continue;
}
- int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+ int fd = unix_open(dev_name, O_RDONLY | O_CLOEXEC);
if (fd == -1) {
continue;
}
@@ -323,7 +324,7 @@
h->urb_out_busy = true;
while (true) {
- auto now = std::chrono::system_clock::now();
+ auto now = std::chrono::steady_clock::now();
if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
// TODO: call USBDEVFS_DISCARDURB?
errno = ETIMEDOUT;
@@ -457,6 +458,11 @@
return orig_len - len;
}
+void usb_reset(usb_handle* h) {
+ ioctl(h->fd, USBDEVFS_RESET);
+ usb_kick(h);
+}
+
void usb_kick(usb_handle* h) {
std::lock_guard<std::mutex> lock(h->mutex);
D("[ kicking %p (fd = %d) ]", h, h->fd);
@@ -535,10 +541,10 @@
// Initialize mark so we don't get garbage collected after the device scan.
usb->mark = true;
- usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+ usb->fd = unix_open(usb->path, O_RDWR | O_CLOEXEC);
if (usb->fd == -1) {
// Opening RW failed, so see if we have RO access.
- usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+ usb->fd = unix_open(usb->path, O_RDONLY | O_CLOEXEC);
if (usb->fd == -1) {
D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
return;
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 49baf36..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -136,8 +136,8 @@
io_service_t usbDevice;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface = NULL;
- IOUSBInterfaceInterface220 **iface = NULL;
- IOUSBDeviceInterface197 **dev = NULL;
+ IOUSBInterfaceInterface500 **iface = NULL;
+ IOUSBDeviceInterface500 **dev = NULL;
HRESULT result;
SInt32 score;
uint32_t locationId;
@@ -163,7 +163,7 @@
//* This gets us the interface object
result = (*plugInInterface)->QueryInterface(
plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500), (LPVOID*)&iface);
//* We only needed the plugin to get the interface, so discard it
(*plugInInterface)->Release(plugInInterface);
if (result || !iface) {
@@ -209,7 +209,7 @@
}
result = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID*)&dev);
//* only needed this to query the plugin
(*plugInInterface)->Release(plugInInterface);
if (result || !dev) {
@@ -556,6 +556,11 @@
return 0;
}
+void usb_reset(usb_handle* handle) {
+ // Unimplemented on OS X.
+ usb_kick(handle);
+}
+
static void usb_kick_locked(usb_handle *handle)
{
LOG(INFO) << "Kicking handle";
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index cfa5cf4..f23c3a5 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -448,6 +448,11 @@
}
}
+void usb_reset(usb_handle* handle) {
+ // Unimplemented on Windows.
+ usb_kick(handle);
+}
+
static void usb_kick_locked(usb_handle* handle) {
// The reason the lock must be acquired before calling this function is in
// case multiple threads are trying to kick the same device at the same time.
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
new file mode 100644
index 0000000..425438e
--- /dev/null
+++ b/adb/daemon/abb.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/wait.h>
+
+#include <android-base/cmsg.h>
+#include <android-base/strings.h>
+#include <cmd.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+namespace {
+
+class AdbFdTextOutput : public android::TextOutput {
+ public:
+ explicit AdbFdTextOutput(borrowed_fd fd) : fd_(fd) {}
+
+ private:
+ android::status_t print(const char* txt, size_t len) override {
+ return WriteFdExactly(fd_, txt, len) ? android::OK : -errno;
+ }
+ void moveIndent(int delta) override { /*not implemented*/
+ }
+
+ void pushBundle() override { /*not implemented*/
+ }
+ void popBundle() override { /*not implemented*/
+ }
+
+ private:
+ borrowed_fd fd_;
+};
+
+std::vector<std::string_view> parseCmdArgs(std::string_view args) {
+ std::vector<std::string_view> argv;
+
+ char delim = ABB_ARG_DELIMETER;
+ size_t size = args.size();
+ size_t base = 0;
+ while (base < size) {
+ size_t found;
+ for (found = base; found < size && args[found] && args[found] != delim; ++found)
+ ;
+ if (found > base) {
+ argv.emplace_back(args.substr(base, found - base));
+ }
+ base = found + 1;
+ }
+
+ return argv;
+}
+
+} // namespace
+
+static int execCmd(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err) {
+ AdbFdTextOutput oin(out);
+ AdbFdTextOutput oerr(err);
+ return cmdMain(parseCmdArgs(args), oin, oerr, in.get(), out.get(), err.get(),
+ RunMode::kLibrary);
+}
+
+int main(int argc, char* const argv[]) {
+ signal(SIGPIPE, SIG_IGN);
+
+ int fd = STDIN_FILENO;
+ std::string data;
+ while (true) {
+ std::string error;
+ if (!ReadProtocolString(fd, &data, &error)) {
+ PLOG(ERROR) << "Failed to read message: " << error;
+ break;
+ }
+
+ std::string_view name = data;
+ auto protocol = SubprocessProtocol::kShell;
+ if (android::base::ConsumePrefix(&name, "abb:")) {
+ protocol = SubprocessProtocol::kShell;
+ } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
+ protocol = SubprocessProtocol::kNone;
+ } else {
+ LOG(FATAL) << "Unknown command prefix for abb: " << data;
+ }
+
+ unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+ if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
+ PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
+ break;
+ }
+ }
+}
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
new file mode 100644
index 0000000..a435279
--- /dev/null
+++ b/adb/daemon/abb_service.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "shell_service.h"
+
+#include <android-base/cmsg.h>
+
+namespace {
+
+struct AbbProcess;
+static auto& abbp = *new std::unique_ptr<AbbProcess>(std::make_unique<AbbProcess>());
+
+struct AbbProcess {
+ unique_fd sendCommand(std::string_view command);
+
+ private:
+ static unique_fd startAbbProcess(unique_fd* error_fd);
+
+ static constexpr auto kRetries = 2;
+ static constexpr auto kErrorProtocol = SubprocessProtocol::kShell;
+
+ std::mutex locker_;
+ unique_fd socket_fd_;
+};
+
+unique_fd AbbProcess::sendCommand(std::string_view command) {
+ std::unique_lock lock{locker_};
+
+ for (int i = 0; i < kRetries; ++i) {
+ unique_fd error_fd;
+ if (socket_fd_ == -1) {
+ socket_fd_ = startAbbProcess(&error_fd);
+ }
+ if (socket_fd_ == -1) {
+ LOG(ERROR) << "failed to start abb process";
+ return error_fd;
+ }
+
+ if (!SendProtocolString(socket_fd_, std::string(command))) {
+ PLOG(ERROR) << "failed to send command to abb";
+ socket_fd_.reset();
+ continue;
+ }
+
+ unique_fd fd;
+ std::string error;
+ char buf;
+ if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
+ PLOG(ERROR) << "failed to receive FD from abb";
+ socket_fd_.reset();
+ continue;
+ }
+
+ return fd;
+ }
+
+ LOG(ERROR) << "abb is unavailable";
+ socket_fd_.reset();
+ return ReportError(kErrorProtocol, "abb is unavailable");
+}
+
+unique_fd AbbProcess::startAbbProcess(unique_fd* error_fd) {
+ constexpr auto abb_process_type = SubprocessType::kRaw;
+ constexpr auto abb_protocol = SubprocessProtocol::kNone;
+ constexpr auto make_pty_raw = false;
+ return StartSubprocess("abb", "dumb", abb_process_type, abb_protocol, make_pty_raw,
+ kErrorProtocol, error_fd);
+}
+
+} // namespace
+
+unique_fd execute_abb_command(std::string_view command) {
+ return abbp->sendCommand(command);
+}
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 180df8f..2b8f461 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -18,7 +18,8 @@
#include "adb.h"
#include "adb_auth.h"
-#include "fdevent.h"
+#include "adb_io.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
#include "transport.h"
@@ -67,7 +68,7 @@
// b64_pton requires one additional byte in the target buffer for
// decoding to succeed. See http://b/28035006 for details.
uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
- if (__b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
+ if (b64_pton(line.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
LOG(ERROR) << "Invalid base64 key " << line.c_str() << " in " << path;
continue;
}
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index d55096a..0e70d47 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -22,13 +22,12 @@
#include <dirent.h>
#include <errno.h>
-#include <linux/xattr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
-#include <sys/xattr.h>
#include <unistd.h>
#include <utime.h>
@@ -37,11 +36,17 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+
+#if defined(__ANDROID__)
#include <selinux/android.h>
+#include <sys/xattr.h>
+#endif
#include "adb.h"
#include "adb_io.h"
@@ -55,11 +60,17 @@
using android::base::StringPrintf;
static bool should_use_fs_config(const std::string& path) {
+#if defined(__ANDROID__)
// TODO: use fs_config to configure permissions on /data too.
return !android::base::StartsWith(path, "/data/");
+#else
+ UNUSED(path);
+ return false;
+#endif
}
static bool update_capabilities(const char* path, uint64_t capabilities) {
+#if defined(__ANDROID__)
if (capabilities == 0) {
// Ensure we clean up in case the capabilities weren't 0 in the past.
removexattr(path, XATTR_NAME_CAPS);
@@ -73,6 +84,10 @@
cap_data.data[1].permitted = (capabilities >> 32);
cap_data.data[1].inheritable = 0;
return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+#else
+ UNUSED(path, capabilities);
+ return true;
+#endif
}
static bool secure_mkdirs(const std::string& path) {
@@ -105,8 +120,10 @@
} else {
if (chown(partial_path.c_str(), uid, gid) == -1) return false;
+#if defined(__ANDROID__)
// Not all filesystems support setting SELinux labels. http://b/23530370.
selinux_android_restorecon(partial_path.c_str(), 0);
+#endif
if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
}
@@ -209,46 +226,50 @@
return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
}
-static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
- mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uint32_t* timestamp, uid_t uid, gid_t gid,
+ uint64_t capabilities, mode_t mode, std::vector<char>& buffer,
+ bool do_unlink) {
+ int rc;
syncmsg msg;
- unsigned int timestamp = 0;
__android_log_security_bswrite(SEC_TAG_ADB_SEND_FILE, path);
- int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
-
- if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) <
- 0) {
- D("[ Failed to fadvise: %d ]", errno);
- }
+ unique_fd fd(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
if (fd < 0 && errno == ENOENT) {
if (!secure_mkdirs(Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
goto fail;
}
- fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+ fd.reset(adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode));
}
if (fd < 0 && errno == EEXIST) {
- fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
+ fd.reset(adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode));
}
if (fd < 0) {
SendSyncFailErrno(s, "couldn't create file");
goto fail;
} else {
- if (fchown(fd, uid, gid) == -1) {
+ if (fchown(fd.get(), uid, gid) == -1) {
SendSyncFailErrno(s, "fchown failed");
goto fail;
}
+#if defined(__ANDROID__)
// Not all filesystems support setting SELinux labels. http://b/23530370.
selinux_android_restorecon(path, 0);
+#endif
// fchown clears the setuid bit - restore it if present.
// Ignore the result of calling fchmod. It's not supported
// by all filesystems, so we don't check for success. b/12441485
- fchmod(fd, mode);
+ fchmod(fd.get(), mode);
+ }
+
+ rc = posix_fadvise(fd.get(), 0, 0,
+ POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
}
while (true) {
@@ -256,7 +277,7 @@
if (msg.data.id != ID_DATA) {
if (msg.data.id == ID_DONE) {
- timestamp = msg.data.size;
+ *timestamp = msg.data.size;
break;
}
SendSyncFail(s, "invalid data message");
@@ -270,24 +291,17 @@
if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
- if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
+ if (!WriteFdExactly(fd.get(), &buffer[0], msg.data.size)) {
SendSyncFailErrno(s, "write failed");
goto fail;
}
}
- adb_close(fd);
-
if (!update_capabilities(path, capabilities)) {
SendSyncFailErrno(s, "update_capabilities failed");
goto fail;
}
- utimbuf u;
- u.actime = timestamp;
- u.modtime = timestamp;
- utime(path, &u);
-
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
return WriteFdExactly(s, &msg.status, sizeof(msg.status));
@@ -322,15 +336,17 @@
}
abort:
- if (fd >= 0) adb_close(fd);
if (do_unlink) adb_unlink(path);
return false;
}
#if defined(_WIN32)
-extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path,
+ uint32_t* timestamp, std::vector<char>& buffer)
+ __attribute__((error("no symlinks on Windows")));
#else
-static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
+static bool handle_send_link(int s, const std::string& path, uint32_t* timestamp,
+ std::vector<char>& buffer) {
syncmsg msg;
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
@@ -367,6 +383,7 @@
if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
if (msg.data.id == ID_DONE) {
+ *timestamp = msg.data.size;
msg.status.id = ID_OKAY;
msg.status.msglen = 0;
if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
@@ -404,58 +421,71 @@
adb_unlink(path.c_str());
}
+ bool result;
+ uint32_t timestamp;
if (S_ISLNK(mode)) {
- return handle_send_link(s, path.c_str(), buffer);
+ result = handle_send_link(s, path, ×tamp, buffer);
+ } else {
+ // Copy user permission bits to "group" and "other" permissions.
+ mode &= 0777;
+ mode |= ((mode >> 3) & 0070);
+ mode |= ((mode >> 3) & 0007);
+
+ uid_t uid = -1;
+ gid_t gid = -1;
+ uint64_t capabilities = 0;
+ if (should_use_fs_config(path)) {
+ unsigned int broken_api_hack = mode;
+ fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
+ mode = broken_api_hack;
+ }
+
+ result = handle_send_file(s, path.c_str(), ×tamp, uid, gid, capabilities, mode, buffer,
+ do_unlink);
}
- // Copy user permission bits to "group" and "other" permissions.
- mode &= 0777;
- mode |= ((mode >> 3) & 0070);
- mode |= ((mode >> 3) & 0007);
-
- uid_t uid = -1;
- gid_t gid = -1;
- uint64_t capabilities = 0;
- if (should_use_fs_config(path)) {
- unsigned int broken_api_hack = mode;
- fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
- mode = broken_api_hack;
+ if (!result) {
+ return false;
}
- return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
+
+ struct timeval tv[2];
+ tv[0].tv_sec = timestamp;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = timestamp;
+ tv[1].tv_usec = 0;
+ lutimes(path.c_str(), tv);
+ return true;
}
static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
__android_log_security_bswrite(SEC_TAG_ADB_RECV_FILE, path);
- int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+ unique_fd fd(adb_open(path, O_RDONLY | O_CLOEXEC));
if (fd < 0) {
SendSyncFailErrno(s, "open failed");
return false;
}
- if (posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
- D("[ Failed to fadvise: %d ]", errno);
+ int rc = posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+ if (rc != 0) {
+ D("[ Failed to fadvise: %s ]", strerror(rc));
}
syncmsg msg;
msg.data.id = ID_DATA;
while (true) {
- int r = adb_read(fd, &buffer[0], buffer.size() - sizeof(msg.data));
+ int r = adb_read(fd.get(), &buffer[0], buffer.size() - sizeof(msg.data));
if (r <= 0) {
if (r == 0) break;
SendSyncFailErrno(s, "read failed");
- adb_close(fd);
return false;
}
msg.data.size = r;
if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
- adb_close(fd);
return false;
}
}
- adb_close(fd);
-
msg.data.id = ID_DONE;
msg.data.size = 0;
return WriteFdExactly(s, &msg.data, sizeof(msg.data));
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 2a6418a..676f8e9 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -33,7 +33,6 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_utils.h"
-#include "fdevent.h"
/* TODO:
** - sync with vsync to avoid tearing
diff --git a/adb/daemon/framebuffer_service.h b/adb/daemon/framebuffer_service.h
index 264da59..bab44be 100644
--- a/adb/daemon/framebuffer_service.h
+++ b/adb/daemon/framebuffer_service.h
@@ -18,4 +18,6 @@
#include "adb_unique_fd.h"
+#if defined(__ANDROID__)
void framebuffer_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 3213f69..2204246 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -16,6 +16,8 @@
* limitations under the License.
*/
+#include <linux/usb/functionfs.h>
+
#include <atomic>
#include <condition_variable>
#include <mutex>
@@ -43,7 +45,7 @@
bool open_new_connection = true;
int (*write)(usb_handle* h, const void* data, int len);
- int (*read)(usb_handle* h, void* data, int len);
+ int (*read)(usb_handle* h, void* data, int len, bool allow_partial);
void (*kick)(usb_handle* h);
void (*close)(usb_handle* h);
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 1363976..b92a7de 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -30,13 +30,21 @@
#include <list>
#include <memory>
+#include <thread>
#include <vector>
+#include <adbconnection/server.h>
+#include <android-base/cmsg.h>
+#include <android-base/unique_fd.h>
+
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
/* here's how these things work.
when adbd starts, it creates a unix server socket
@@ -131,16 +139,16 @@
static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
struct JdwpProcess {
- explicit JdwpProcess(int socket) {
+ JdwpProcess(unique_fd socket, pid_t pid) {
+ CHECK(pid != 0);
+
this->socket = socket;
- this->fde = fdevent_create(socket, jdwp_process_event, this);
+ this->pid = pid;
+ this->fde = fdevent_create(socket.release(), jdwp_process_event, this);
if (!this->fde) {
LOG(FATAL) << "could not create fdevent for new JDWP process";
}
-
- /* start by waiting for the PID */
- fdevent_add(this->fde, FDE_READ);
}
~JdwpProcess() {
@@ -158,18 +166,12 @@
}
void RemoveFromList() {
- if (this->pid >= 0) {
- D("removing pid %d from jdwp process list", this->pid);
- } else {
- D("removing transient JdwpProcess from list");
- }
-
auto pred = [this](const auto& proc) { return proc.get() == this; };
_jdwp_list.remove_if(pred);
}
+ borrowed_fd socket = -1;
int32_t pid = -1;
- int socket = -1;
fdevent* fde = nullptr;
std::vector<unique_fd> out_fds;
@@ -179,11 +181,6 @@
std::string temp;
for (auto& proc : _jdwp_list) {
- /* skip transient connections */
- if (proc->pid < 0) {
- continue;
- }
-
std::string next = std::to_string(proc->pid) + "\n";
if (temp.length() + next.length() > bufferlen) {
D("truncating JDWP process list (max len = %zu)", bufferlen);
@@ -212,24 +209,12 @@
static void jdwp_process_event(int socket, unsigned events, void* _proc) {
JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
- CHECK_EQ(socket, proc->socket);
+ CHECK_EQ(socket, proc->socket.get());
if (events & FDE_READ) {
- if (proc->pid < 0) {
- ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, &proc->pid, sizeof(proc->pid), 0));
- if (rc != sizeof(proc->pid)) {
- D("failed to read jdwp pid: rc = %zd, errno = %s", rc, strerror(errno));
- goto CloseProcess;
- }
-
- /* all is well, keep reading to detect connection closure */
- D("Adding pid %d to jdwp process list", proc->pid);
- jdwp_process_list_updated();
- } else {
- // We already have the PID, if we can read from the socket, we've probably hit EOF.
- D("terminating JDWP connection %d", proc->pid);
- goto CloseProcess;
- }
+ // We already have the PID, if we can read from the socket, we've probably hit EOF.
+ D("terminating JDWP connection %d", proc->pid);
+ goto CloseProcess;
}
if (events & FDE_WRITE) {
@@ -237,30 +222,7 @@
CHECK(!proc->out_fds.empty());
int fd = proc->out_fds.back().get();
- struct cmsghdr* cmsg;
- struct msghdr msg;
- struct iovec iov;
- char dummy = '!';
- char buffer[sizeof(struct cmsghdr) + sizeof(int)];
-
- iov.iov_base = &dummy;
- iov.iov_len = 1;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
- msg.msg_control = buffer;
- msg.msg_controllen = sizeof(buffer);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = msg.msg_controllen;
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- ((int*)CMSG_DATA(cmsg))[0] = fd;
-
- int ret = TEMP_FAILURE_RETRY(sendmsg(socket, &msg, 0));
- if (ret < 0) {
+ if (android::base::SendFileDescriptors(socket, "", 1, fd) != 1) {
D("sending new file descriptor to JDWP %d failed: %s", proc->pid, strerror(errno));
goto CloseProcess;
}
@@ -305,103 +267,6 @@
return unique_fd{};
}
-/** VM DEBUG CONTROL SOCKET
- **
- ** we do implement a custom asocket to receive the data
- **/
-
-/* name of the debug control Unix socket */
-#define JDWP_CONTROL_NAME "\0jdwp-control"
-#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
-
-struct JdwpControl {
- int listen_socket;
- fdevent* fde;
-};
-
-static JdwpControl _jdwp_control;
-
-static void jdwp_control_event(int s, unsigned events, void* user);
-
-static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
- sockaddr_un addr;
- socklen_t addrlen;
- int s;
- int maxpath = sizeof(addr.sun_path);
- int pathlen = socknamelen;
-
- if (pathlen >= maxpath) {
- D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- memcpy(addr.sun_path, sockname, socknamelen);
-
- s = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
- if (s < 0) {
- D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
- return -1;
- }
-
- addrlen = pathlen + sizeof(addr.sun_family);
-
- if (bind(s, reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
- D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
- adb_close(s);
- return -1;
- }
-
- if (listen(s, 4) < 0) {
- D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
- adb_close(s);
- return -1;
- }
-
- control->listen_socket = s;
-
- control->fde = fdevent_create(s, jdwp_control_event, control);
- if (control->fde == nullptr) {
- D("could not create fdevent for jdwp control socket");
- adb_close(s);
- return -1;
- }
-
- /* only wait for incoming connections */
- fdevent_add(control->fde, FDE_READ);
-
- D("jdwp control socket started (%d)", control->listen_socket);
- return 0;
-}
-
-static void jdwp_control_event(int fd, unsigned events, void* _control) {
- JdwpControl* control = (JdwpControl*)_control;
-
- CHECK_EQ(fd, control->listen_socket);
- if (events & FDE_READ) {
- int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
- if (s < 0) {
- if (errno == ECONNABORTED) {
- /* oops, the JDWP process died really quick */
- D("oops, the JDWP process died really quick");
- return;
- } else {
- /* the socket is probably closed ? */
- D("weird accept() failed on jdwp control socket: %s", strerror(errno));
- return;
- }
- }
-
- auto proc = std::make_unique<JdwpProcess>(s);
- if (!proc) {
- LOG(FATAL) << "failed to allocate JdwpProcess";
- }
-
- _jdwp_list.emplace_back(std::move(proc));
- }
-}
-
/** "jdwp" local service implementation
** this simply returns the list of known JDWP process pids
**/
@@ -552,7 +417,22 @@
}
int init_jdwp(void) {
- return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
+ std::thread([]() {
+ adb_thread_setname("jdwp control");
+ adbconnection_listen([](int fd, pid_t pid) {
+ LOG(INFO) << "jdwp connection from " << pid;
+ fdevent_run_on_main_thread([fd, pid] {
+ unique_fd ufd(fd);
+ auto proc = std::make_unique<JdwpProcess>(std::move(ufd), pid);
+ if (!proc) {
+ LOG(FATAL) << "failed to allocate JdwpProcess";
+ }
+ _jdwp_list.emplace_back(std::move(proc));
+ jdwp_process_list_updated();
+ });
+ });
+ }).detach();
+ return 0;
}
#endif /* !ADB_HOST */
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index f6f1acc..e5a4917 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -18,7 +18,10 @@
#include "sysdeps.h"
+#if defined(__BIONIC__)
#include <android/fdsan.h>
+#endif
+
#include <errno.h>
#include <getopt.h>
#include <malloc.h>
@@ -34,12 +37,15 @@
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+
+#if defined(__ANDROID__)
#include <libminijail.h>
#include <log/log_properties.h>
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
#include "selinux/android.h"
+#endif
#include "adb.h"
#include "adb_auth.h"
@@ -49,19 +55,26 @@
#include "mdns.h"
+#if defined(__ANDROID__)
static const char* root_seclabel = nullptr;
+static inline bool is_device_unlocked() {
+ return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
+}
+
static bool should_drop_capabilities_bounding_set() {
-#if defined(ALLOW_ADBD_ROOT)
- if (__android_log_is_debuggable()) {
- return false;
+ if (ALLOW_ADBD_ROOT || is_device_unlocked()) {
+ if (__android_log_is_debuggable()) {
+ return false;
+ }
}
-#endif
return true;
}
static bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
+ // "adb root" not allowed, always drop privileges.
+ if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
+
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
@@ -91,9 +104,6 @@
}
return drop;
-#else
- return true; // "adb root" not allowed, always drop privileges.
-#endif // ALLOW_ADBD_ROOT
}
static void drop_privileges(int server_port) {
@@ -167,10 +177,14 @@
}
}
}
+#endif
static void setup_port(int port) {
+ LOG(INFO) << "adbd listening on port " << port;
local_init(port);
+#if defined(__ANDROID__)
setup_mdns(port);
+#endif
}
int adbd_main(int server_port) {
@@ -178,10 +192,12 @@
signal(SIGPIPE, SIG_IGN);
+#if defined(__BIONIC__)
auto fdsan_level = android_fdsan_get_error_level();
if (fdsan_level == ANDROID_FDSAN_ERROR_LEVEL_DISABLED) {
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
}
+#endif
init_transport_registration();
@@ -192,6 +208,10 @@
#if defined(ALLOW_ADBD_NO_AUTH)
// If ro.adb.secure is unset, default to no authentication required.
auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+#elif defined(__ANDROID__)
+ if (is_device_unlocked()) { // allows no authentication when the device is unlocked.
+ auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+ }
#endif
adbd_auth_init();
@@ -206,14 +226,19 @@
" unchanged.\n");
}
+#if defined(__ANDROID__)
drop_privileges(server_port);
+#endif
bool is_usb = false;
+
+#if defined(__ANDROID__)
if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
}
+#endif
// If one of these properties is set, also listen on that port.
// If one of the properties isn't set and we couldn't listen on usb, listen
@@ -244,8 +269,10 @@
}
int main(int argc, char** argv) {
+#if defined(__BIONIC__)
// Set M_DECAY_TIME so that our allocations aren't immediately purged on free.
mallopt(M_DECAY_TIME, 1);
+#endif
while (true) {
static struct option opts[] = {
@@ -261,19 +288,21 @@
}
switch (c) {
- case 's':
- root_seclabel = optarg;
- break;
- case 'b':
- adb_device_banner = optarg;
- break;
- case 'v':
- printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
- ADB_VERSION_MINOR, ADB_SERVER_VERSION);
- return 0;
- default:
- // getopt already prints "adbd: invalid option -- %c" for us.
- return 1;
+#if defined(__ANDROID__)
+ case 's':
+ root_seclabel = optarg;
+ break;
+#endif
+ case 'b':
+ adb_device_banner = optarg;
+ break;
+ case 'v':
+ printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
+ ADB_VERSION_MINOR, ADB_SERVER_VERSION);
+ return 0;
+ default:
+ // getopt already prints "adbd: invalid option -- %c" for us.
+ return 1;
}
}
diff --git a/adb/daemon/mdns.cpp b/adb/daemon/mdns.cpp
index 849378f..3530f48 100644
--- a/adb/daemon/mdns.cpp
+++ b/adb/daemon/mdns.cpp
@@ -74,7 +74,7 @@
if (error != kDNSServiceErr_NoError) {
LOG(ERROR) << "Could not register mDNS service (" << error << ").";
- mdns_registered = false;
+ return;
}
mdns_registered = true;
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
new file mode 100644
index 0000000..13398af
--- /dev/null
+++ b/adb/daemon/reboot_service.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+void reboot_service(unique_fd fd, const std::string& arg) {
+ std::string reboot_arg = arg;
+ sync();
+
+ if (reboot_arg.empty()) reboot_arg = "adb";
+ std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
+
+ if (reboot_arg == "fastboot" &&
+ android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+ access("/dev/socket/recovery", F_OK) == 0) {
+ LOG(INFO) << "Recovery specific reboot fastboot";
+ /*
+ * The socket is created to allow switching between recovery and
+ * fastboot.
+ */
+ android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
+ if (sock < 0) {
+ WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
+ PLOG(ERROR) << "Creating recovery socket failed";
+ return;
+ }
+
+ sockaddr_un addr = {.sun_family = AF_UNIX};
+ strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
+ if (connect(sock.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+ WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
+ PLOG(ERROR) << "Couldn't connect to recovery socket";
+ return;
+ }
+ const char msg_switch_to_fastboot = 'f';
+ auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
+ if (ret != sizeof(msg_switch_to_fastboot)) {
+ WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
+ PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
+ return;
+ }
+ } else {
+ if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
+ WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
+ return;
+ }
+ }
+ // Don't return early. Give the reboot command time to take effect
+ // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
+ while (true) {
+ pause();
+ }
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/adb/daemon/reboot_service.h
similarity index 77%
rename from mkbootimg/mkbootimg_dummy.cpp
rename to adb/daemon/reboot_service.h
index 410d379..f68913e 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/adb/daemon/reboot_service.h
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include <string>
+
+#include "adb_unique_fd.h"
+
+#if defined(__ANDROID__)
+void reboot_service(unique_fd fd, const std::string& arg);
+#endif
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ae02525..ce494ee 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -14,302 +14,75 @@
* limitations under the License.
*/
-#define TRACE_TAG ADB
-
-#include "sysdeps.h"
-
#include <errno.h>
#include <fcntl.h>
-#include <mntent.h>
-#include <spawn.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/mount.h>
-#include <sys/statvfs.h>
-#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include <memory>
-#include <set>
#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "set_verity_enable_state_service.h"
-using android::base::Realpath;
+static constexpr char kRemountCmd[] = "/system/bin/remount";
-// Returns the last device used to mount a directory in /proc/mounts.
-// This will find overlayfs entry where upperdir=lowerdir, to make sure
-// remount is associated with the correct directory.
-static std::string find_proc_mount(const char* dir) {
- std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
- std::string mnt_fsname;
- if (!fp) return mnt_fsname;
-
- // dir might be a symlink, e.g., /product -> /system/product in GSI.
- std::string canonical_path;
- if (!Realpath(dir, &canonical_path)) {
- PLOG(ERROR) << "Realpath failed: " << dir;
- }
-
- mntent* e;
- while ((e = getmntent(fp.get())) != nullptr) {
- if (canonical_path == e->mnt_dir) {
- mnt_fsname = e->mnt_fsname;
- }
- }
- return mnt_fsname;
-}
-
-// Returns the device used to mount a directory in the fstab.
-static std::string find_fstab_mount(const char* dir) {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
- return rec ? rec->blk_device : "";
-}
-
-// The proc entry for / is full of lies, so check fstab instead.
-// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir, bool is_root) {
- if (is_root) {
- return find_fstab_mount(dir);
- } else {
- return find_proc_mount(dir);
- }
-}
-
-bool make_block_device_writable(const std::string& dev) {
- if ((dev == "overlay") || (dev == "overlayfs")) return true;
- int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
+static bool do_remount(int fd, const std::string& cmd) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
return false;
}
- int OFF = 0;
- bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
- unix_close(fd);
- return result;
-}
-
-static bool can_unshare_blocks(int fd, const char* dev) {
- const char* E2FSCK_BIN = "/system/bin/e2fsck";
- if (access(E2FSCK_BIN, X_OK)) {
- WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev);
+ auto pid = fork();
+ if (pid < 0) {
+ WriteFdFmt(fd, "Failed to fork to %s: %s\n", kRemountCmd, strerror(errno));
return false;
}
- pid_t child;
- char* env[] = {nullptr};
- const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr};
- if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast<char**>(argv), env)) {
- WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno));
+ if (pid == 0) {
+ // child side of the fork
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+
+ execl(kRemountCmd, kRemountCmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr);
+ _exit(errno);
+ }
+
+ int wstatus = 0;
+ auto ret = waitpid(pid, &wstatus, 0);
+
+ if (ret == -1) {
+ WriteFdFmt(fd, "Failed to wait for %s: %s\n", kRemountCmd, strerror(errno));
+ return false;
+ } else if (ret != pid) {
+ WriteFdFmt(fd, "pid %d and waitpid return %d do not match for %s\n",
+ static_cast<int>(pid), static_cast<int>(ret), kRemountCmd);
return false;
}
- int status = 0;
- int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
- if (ret < 0) {
- WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno));
+
+ if (WIFSIGNALED(wstatus)) {
+ WriteFdFmt(fd, "%s terminated with signal %s\n", kRemountCmd,
+ strsignal(WTERMSIG(wstatus)));
return false;
}
- if (!WIFEXITED(status)) {
- WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+
+ if (!WIFEXITED(wstatus)) {
+ WriteFdFmt(fd, "%s stopped with status 0x%x\n", kRemountCmd, wstatus);
return false;
}
- int rc = WEXITSTATUS(status);
- if (rc != 0) {
- WriteFdFmt(fd,
- "%s is deduplicated, and an e2fsck check failed. It might not "
- "have enough free-space to be remounted as writable.\n",
- dev);
+
+ if (WEXITSTATUS(wstatus)) {
+ WriteFdFmt(fd, "%s exited with status %d\n", kRemountCmd, WEXITSTATUS(wstatus));
return false;
}
+
return true;
}
-static unsigned long get_mount_flags(int fd, const char* dir) {
- struct statvfs st_vfs;
- if (statvfs(dir, &st_vfs) == -1) {
- // Even though we could not get the original mount flags, assume that
- // the mount was originally read-only.
- WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno));
- return MS_RDONLY;
- }
- return st_vfs.f_flag;
-}
-
-static bool remount_partition(int fd, const char* dir) {
- if (!directory_exists(dir)) {
- return true;
- }
- bool is_root = strcmp(dir, "/") == 0;
- if (is_root && !find_mount("/system", false).empty()) {
- dir = "/system";
- is_root = false;
- }
- std::string dev = find_mount(dir, is_root);
- // Even if the device for the root is not found, we still try to remount it
- // as rw. This typically only happens when running Android in a container:
- // the root will almost always be in a loop device, which is dynamic, so
- // it's not convenient to put in the fstab.
- if (dev.empty() && !is_root) {
- return true;
- }
- if (!dev.empty() && !make_block_device_writable(dev)) {
- WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
- dir, dev.c_str(), strerror(errno));
- return false;
- }
-
- unsigned long remount_flags = get_mount_flags(fd, dir);
- remount_flags &= ~MS_RDONLY;
- remount_flags |= MS_REMOUNT;
-
- if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) {
- // This is useful for cases where the superblock is already marked as
- // read-write, but the mount itself is read-only, such as containers
- // where the remount with just MS_REMOUNT is forbidden by the kernel.
- WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
- return false;
- }
- if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
- WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
- return false;
- }
- return true;
-}
-
-static void reboot_for_remount(int fd, bool need_fsck) {
- std::string reboot_cmd = "reboot";
- if (need_fsck) {
- const std::vector<std::string> options = {"--fsck_unshare_blocks"};
- std::string err;
- if (!write_bootloader_message(options, &err)) {
- WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str());
- return;
- }
-
- WriteFdExactly(fd,
- "The device will now reboot to recovery and attempt "
- "un-deduplication.\n");
- reboot_cmd = "reboot,recovery";
- }
-
- sync();
- android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
-}
-
void remount_service(unique_fd fd, const std::string& cmd) {
- bool user_requested_reboot = cmd == "-R";
-
- if (getuid() != 0) {
- WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n");
- return;
- }
-
- bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
- bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
-
- std::vector<std::string> partitions{"/", "/odm", "/oem", "/product_services",
- "/product", "/vendor"};
-
- bool verity_enabled = (system_verified || vendor_verified);
-
- // If we can use overlayfs, lets get it in place first
- // before we struggle with determining deduplication operations.
- if (!verity_enabled && fs_mgr_overlayfs_setup()) {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (fs_mgr_overlayfs_mount_all(fstab.get())) {
- WriteFdExactly(fd.get(), "overlayfs mounted\n");
- }
- }
-
- // Find partitions that are deduplicated, and can be un-deduplicated.
- std::set<std::string> dedup;
- for (const auto& part : partitions) {
- auto partition = part;
- if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
- std::string dev = find_mount(partition.c_str(), partition == "/");
- if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
- continue;
- }
- if (can_unshare_blocks(fd.get(), dev.c_str())) {
- dedup.emplace(partition);
- }
- }
-
- // Reboot now if the user requested it (and an operation needs a reboot).
- if (user_requested_reboot) {
- if (!dedup.empty() || verity_enabled) {
- if (verity_enabled) {
- set_verity_enabled_state_service(unique_fd(dup(fd.get())), false);
- }
- reboot_for_remount(fd.get(), !dedup.empty());
- return;
- }
- WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n");
- }
-
- // If we need to disable-verity, but we also need to perform a recovery
- // fsck for deduplicated partitions, hold off on warning about verity. We
- // can handle both verity and the recovery fsck in the same reboot cycle.
- if (verity_enabled && dedup.empty()) {
- // Allow remount but warn of likely bad effects
- bool both = system_verified && vendor_verified;
- WriteFdFmt(fd.get(), "dm_verity is enabled on the %s%s%s partition%s.\n",
- system_verified ? "system" : "", both ? " and " : "",
- vendor_verified ? "vendor" : "", both ? "s" : "");
- WriteFdExactly(fd.get(),
- "Use \"adb disable-verity\" to disable verity.\n"
- "If you do not, remount may succeed, however, you will still "
- "not be able to write to these volumes.\n");
- WriteFdExactly(fd.get(),
- "Alternately, use \"adb remount -R\" to disable verity "
- "and automatically reboot.\n");
- }
-
- bool success = true;
- for (const auto& partition : partitions) {
- // Don't try to remount partitions that need an fsck in recovery.
- if (dedup.count(partition)) {
- continue;
- }
- success &= remount_partition(fd.get(), partition.c_str());
- }
-
- if (!dedup.empty()) {
- WriteFdExactly(fd.get(),
- "The following partitions are deduplicated and cannot "
- "yet be remounted:\n");
- for (const std::string& name : dedup) {
- WriteFdFmt(fd.get(), " %s\n", name.c_str());
- }
-
- WriteFdExactly(fd.get(),
- "To reboot and un-deduplicate the listed partitions, "
- "please retry with adb remount -R.\n");
- if (system_verified || vendor_verified) {
- WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n");
- }
- return;
- }
-
- if (!success) {
- WriteFdExactly(fd.get(), "remount failed\n");
- } else {
- WriteFdExactly(fd.get(), "remount succeeded\n");
- }
+ const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
+ WriteFdFmt(fd.get(), "remount %s\n", success);
}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
index e4e2550..522a5da 100644
--- a/adb/daemon/remount_service.h
+++ b/adb/daemon/remount_service.h
@@ -20,5 +20,6 @@
#include "adb_unique_fd.h"
-bool make_block_device_writable(const std::string&);
+#if defined(__ANDROID__)
void remount_service(unique_fd, const std::string&);
+#endif
diff --git a/adb/daemon/restart_service.cpp b/adb/daemon/restart_service.cpp
new file mode 100644
index 0000000..16d2627
--- /dev/null
+++ b/adb/daemon/restart_service.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG SERVICES
+
+#include "sysdeps.h"
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <log/log_properties.h>
+
+#include "adb_io.h"
+#include "adb_unique_fd.h"
+
+void restart_root_service(unique_fd fd) {
+ if (getuid() == 0) {
+ WriteFdExactly(fd.get(), "adbd is already running as root\n");
+ return;
+ }
+ if (!__android_log_is_debuggable()) {
+ WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
+ return;
+ }
+
+ LOG(INFO) << "adbd restarting as root";
+ android::base::SetProperty("service.adb.root", "1");
+ WriteFdExactly(fd.get(), "restarting adbd as root\n");
+}
+
+void restart_unroot_service(unique_fd fd) {
+ if (getuid() != 0) {
+ WriteFdExactly(fd.get(), "adbd not running as root\n");
+ return;
+ }
+
+ LOG(INFO) << "adbd restarting as nonroot";
+ android::base::SetProperty("service.adb.root", "0");
+ WriteFdExactly(fd.get(), "restarting adbd as non root\n");
+}
+
+void restart_tcp_service(unique_fd fd, int port) {
+ if (port <= 0) {
+ WriteFdFmt(fd.get(), "invalid port %d\n", port);
+ return;
+ }
+
+ LOG(INFO) << "adbd restarting in TCP mode (port = " << port << ")";
+ android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
+ WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
+}
+
+void restart_usb_service(unique_fd fd) {
+ LOG(INFO) << "adbd restarting in USB mode";
+ android::base::SetProperty("service.adb.tcp.port", "0");
+ WriteFdExactly(fd.get(), "restarting in USB mode\n");
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/adb/daemon/restart_service.h
similarity index 64%
copy from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
copy to adb/daemon/restart_service.h
index 5d0d924..19840bd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/adb/daemon/restart_service.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+#pragma once
-#include <stdint.h>
-#include <string>
+#include "adb_unique_fd.h"
-namespace unwindstack {
-class Memory;
-}
-
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
+#if defined(__ANDROID__)
+void restart_root_service(unique_fd fd);
+void restart_unroot_service(unique_fd fd);
+void restart_tcp_service(unique_fd fd, int port);
+void restart_usb_service(unique_fd fd);
+#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 720ec6a..e6f4499 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -33,13 +33,12 @@
#include <thread>
#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <android-base/parsenetaddress.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <bootloader_message/bootloader_message.h>
-#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <log/log_properties.h>
@@ -54,110 +53,29 @@
#include "daemon/file_sync_service.h"
#include "daemon/framebuffer_service.h"
+#include "daemon/reboot_service.h"
#include "daemon/remount_service.h"
+#include "daemon/restart_service.h"
#include "daemon/set_verity_enable_state_service.h"
#include "daemon/shell_service.h"
-void restart_root_service(unique_fd fd) {
- if (getuid() == 0) {
- WriteFdExactly(fd.get(), "adbd is already running as root\n");
- return;
- }
- if (!__android_log_is_debuggable()) {
- WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");
- return;
- }
-
- android::base::SetProperty("service.adb.root", "1");
- WriteFdExactly(fd.get(), "restarting adbd as root\n");
-}
-
-void restart_unroot_service(unique_fd fd) {
- if (getuid() != 0) {
- WriteFdExactly(fd.get(), "adbd not running as root\n");
- return;
- }
- android::base::SetProperty("service.adb.root", "0");
- WriteFdExactly(fd.get(), "restarting adbd as non root\n");
-}
-
-void restart_tcp_service(unique_fd fd, int port) {
- if (port <= 0) {
- WriteFdFmt(fd.get(), "invalid port %d\n", port);
- return;
- }
-
- android::base::SetProperty("service.adb.tcp.port", android::base::StringPrintf("%d", port));
- WriteFdFmt(fd.get(), "restarting in TCP mode port: %d\n", port);
-}
-
-void restart_usb_service(unique_fd fd) {
- android::base::SetProperty("service.adb.tcp.port", "0");
- WriteFdExactly(fd.get(), "restarting in USB mode\n");
-}
-
-void reboot_service(unique_fd fd, const std::string& arg) {
- std::string reboot_arg = arg;
- sync();
-
- if (reboot_arg.empty()) reboot_arg = "adb";
- std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
-
- if (reboot_arg == "fastboot" &&
- android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
- access("/dev/socket/recovery", F_OK) == 0) {
- LOG(INFO) << "Recovery specific reboot fastboot";
- /*
- * The socket is created to allow switching between recovery and
- * fastboot.
- */
- android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
- if (sock < 0) {
- WriteFdFmt(fd, "reboot (%s) create\n", strerror(errno));
- PLOG(ERROR) << "Creating recovery socket failed";
- return;
- }
-
- sockaddr_un addr = {.sun_family = AF_UNIX};
- strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
- if (connect(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
- WriteFdFmt(fd, "reboot (%s) connect\n", strerror(errno));
- PLOG(ERROR) << "Couldn't connect to recovery socket";
- return;
- }
- const char msg_switch_to_fastboot = 'f';
- auto ret = adb_write(sock, &msg_switch_to_fastboot, sizeof(msg_switch_to_fastboot));
- if (ret != sizeof(msg_switch_to_fastboot)) {
- WriteFdFmt(fd, "reboot (%s) write\n", strerror(errno));
- PLOG(ERROR) << "Couldn't write message to recovery socket to switch to fastboot";
- return;
- }
- } else {
- if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
- WriteFdFmt(fd.get(), "reboot (%s) failed\n", reboot_string.c_str());
- return;
- }
- }
- // Don't return early. Give the reboot command time to take effect
- // to avoid messing up scripts which do "adb reboot && adb wait-for-device"
- while (true) {
- pause();
- }
-}
void reconnect_service(unique_fd fd, atransport* t) {
WriteFdExactly(fd.get(), "done");
kick_transport(t);
}
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+ // TODO: Switch handle_forward_request to std::string_view.
+ std::string str(command);
+
int s[2];
if (adb_socketpair(s)) {
PLOG(ERROR) << "cannot create service socket pair.";
return unique_fd{};
}
VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (!handle_forward_request(command, transport, s[1])) {
+ if (!handle_forward_request(str.c_str(), transport, s[1])) {
SendFail(s[1], "not a reverse forwarding command");
}
adb_close(s[1]);
@@ -166,15 +84,16 @@
// Shell service string can look like:
// shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view args, const atransport* transport) {
size_t delimiter_index = args.find(':');
if (delimiter_index == std::string::npos) {
LOG(ERROR) << "No ':' found in shell service arguments: " << args;
return unique_fd{};
}
- const std::string service_args = args.substr(0, delimiter_index);
- const std::string command = args.substr(delimiter_index + 1);
+ // TODO: android::base::Split(const std::string_view&, ...)
+ std::string service_args(args.substr(0, delimiter_index));
+ std::string command(args.substr(delimiter_index + 1));
// Defaults:
// PTY for interactive, raw for non-interactive.
@@ -191,15 +110,15 @@
type = SubprocessType::kPty;
} else if (arg == kShellServiceArgShellProtocol) {
protocol = SubprocessProtocol::kShell;
- } else if (android::base::StartsWith(arg, "TERM=")) {
- terminal_type = arg.substr(5);
+ } else if (arg.starts_with("TERM=")) {
+ terminal_type = arg.substr(strlen("TERM="));
} else if (!arg.empty()) {
// This is not an error to allow for future expansion.
LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
}
}
- return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+ return StartSubprocess(command, terminal_type.c_str(), type, protocol);
}
static void spin_service(unique_fd fd) {
@@ -216,66 +135,181 @@
}
fdevent_run_on_main_thread([fd = pipe_read.release()]() {
- fdevent* fde = fdevent_create(fd, [](int, unsigned, void*) {}, nullptr);
+ fdevent* fde = fdevent_create(
+ fd, [](int, unsigned, void*) {}, nullptr);
fdevent_add(fde, FDE_READ);
});
WriteFdExactly(fd.get(), "spinning\n");
}
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
- if (!strncmp("dev:", name, 4)) {
- return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
- } else if (!strncmp(name, "framebuffer:", 12)) {
+struct ServiceSocket : public asocket {
+ ServiceSocket() {
+ install_local_socket(this);
+ this->enqueue = [](asocket* self, apacket::payload_type data) {
+ return static_cast<ServiceSocket*>(self)->Enqueue(std::move(data));
+ };
+ this->ready = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Ready(); };
+ this->close = [](asocket* self) { return static_cast<ServiceSocket*>(self)->Close(); };
+ }
+ virtual ~ServiceSocket() = default;
+
+ virtual int Enqueue(apacket::payload_type data) { return -1; }
+ virtual void Ready() {}
+ virtual void Close() {
+ if (peer) {
+ peer->peer = nullptr;
+ if (peer->shutdown) {
+ peer->shutdown(peer);
+ }
+ peer->close(peer);
+ }
+
+ remove_socket(this);
+ delete this;
+ }
+};
+
+struct SinkSocket : public ServiceSocket {
+ explicit SinkSocket(size_t byte_count) {
+ LOG(INFO) << "Creating new SinkSocket with capacity " << byte_count;
+ bytes_left_ = byte_count;
+ }
+
+ virtual ~SinkSocket() { LOG(INFO) << "SinkSocket destroyed"; }
+
+ virtual int Enqueue(apacket::payload_type data) override final {
+ if (bytes_left_ <= data.size()) {
+ // Done reading.
+ Close();
+ return -1;
+ }
+
+ bytes_left_ -= data.size();
+ return 0;
+ }
+
+ size_t bytes_left_;
+};
+
+struct SourceSocket : public ServiceSocket {
+ explicit SourceSocket(size_t byte_count) {
+ LOG(INFO) << "Creating new SourceSocket with capacity " << byte_count;
+ bytes_left_ = byte_count;
+ }
+
+ virtual ~SourceSocket() { LOG(INFO) << "SourceSocket destroyed"; }
+
+ void Ready() {
+ size_t len = std::min(bytes_left_, get_max_payload());
+ if (len == 0) {
+ Close();
+ return;
+ }
+
+ Block block(len);
+ memset(block.data(), 0, block.size());
+ peer->enqueue(peer, std::move(block));
+ bytes_left_ -= len;
+ }
+
+ int Enqueue(apacket::payload_type data) { return -1; }
+
+ size_t bytes_left_;
+};
+
+asocket* daemon_service_to_socket(std::string_view name) {
+ if (name == "jdwp") {
+ return create_jdwp_service_socket();
+ } else if (name == "track-jdwp") {
+ return create_jdwp_tracker_service_socket();
+ } else if (android::base::ConsumePrefix(&name, "sink:")) {
+ uint64_t byte_count = 0;
+ if (!ParseUint(&byte_count, name)) {
+ return nullptr;
+ }
+ return new SinkSocket(byte_count);
+ } else if (android::base::ConsumePrefix(&name, "source:")) {
+ uint64_t byte_count = 0;
+ if (!ParseUint(&byte_count, name)) {
+ return nullptr;
+ }
+ return new SourceSocket(byte_count);
+ }
+
+ return nullptr;
+}
+
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+ if (name.starts_with("abb:") || name.starts_with("abb_exec:")) {
+ return execute_abb_command(name);
+ }
+#endif
+
+#if defined(__ANDROID__)
+ if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (!strncmp(name, "jdwp:", 5)) {
- return create_jdwp_connection_fd(atoi(name + 5));
- } else if (!strncmp(name, "shell", 5)) {
- return ShellService(name + 5, transport);
- } else if (!strncmp(name, "exec:", 5)) {
- return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "sync:", 5)) {
- return create_service_thread("sync", file_sync_service);
- } else if (!strncmp(name, "remount:", 8)) {
- std::string options(name + strlen("remount:"));
+ } else if (android::base::ConsumePrefix(&name, "remount:")) {
+ std::string arg(name);
return create_service_thread("remount",
- std::bind(remount_service, std::placeholders::_1, options));
- } else if (!strncmp(name, "reboot:", 7)) {
- std::string arg(name + strlen("reboot:"));
+ std::bind(remount_service, std::placeholders::_1, arg));
+ } else if (android::base::ConsumePrefix(&name, "reboot:")) {
+ std::string arg(name);
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
- } else if (!strncmp(name, "root:", 5)) {
+ } else if (name.starts_with("root:")) {
return create_service_thread("root", restart_root_service);
- } else if (!strncmp(name, "unroot:", 7)) {
+ } else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (!strncmp(name, "backup:", 7)) {
- return StartSubprocess(
- android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
- nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "restore:", 8)) {
+ } else if (android::base::ConsumePrefix(&name, "backup:")) {
+ std::string cmd = "/system/bin/bu backup ";
+ cmd += name;
+ return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+ } else if (name.starts_with("restore:")) {
return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
- } else if (!strncmp(name, "tcpip:", 6)) {
+ } else if (name.starts_with("disable-verity:")) {
+ return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
+ std::placeholders::_1, false));
+ } else if (name.starts_with("enable-verity:")) {
+ return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+ std::placeholders::_1, true));
+ } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
+ std::string str(name);
+
int port;
- if (sscanf(name + 6, "%d", &port) != 1) {
+ if (sscanf(str.c_str(), "%d", &port) != 1) {
return unique_fd{};
}
return create_service_thread("tcp",
std::bind(restart_tcp_service, std::placeholders::_1, port));
- } else if (!strncmp(name, "usb:", 4)) {
+ } else if (name.starts_with("usb:")) {
return create_service_thread("usb", restart_usb_service);
- } else if (!strncmp(name, "reverse:", 8)) {
- return reverse_service(name + 8, transport);
- } else if (!strncmp(name, "disable-verity:", 15)) {
- return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, false));
- } else if (!strncmp(name, "enable-verity:", 15)) {
- return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
- std::placeholders::_1, true));
- } else if (!strcmp(name, "reconnect")) {
+ }
+#endif
+
+ if (android::base::ConsumePrefix(&name, "dev:")) {
+ return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+ } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
+ pid_t pid;
+ if (!ParseUint(&pid, name)) {
+ return unique_fd{};
+ }
+ return create_jdwp_connection_fd(pid);
+ } else if (android::base::ConsumePrefix(&name, "shell")) {
+ return ShellService(name, transport);
+ } else if (android::base::ConsumePrefix(&name, "exec:")) {
+ return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
+ } else if (name.starts_with("sync:")) {
+ return create_service_thread("sync", file_sync_service);
+ } else if (android::base::ConsumePrefix(&name, "reverse:")) {
+ return reverse_service(name, transport);
+ } else if (name == "reconnect") {
return create_service_thread(
"reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
- } else if (!strcmp(name, "spin")) {
+ } else if (name == "spin") {
return create_service_thread("spin", spin_service);
}
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 3676de5..4fbccdb 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -25,6 +25,7 @@
#include <libavb_user/libavb_user.h>
#include <stdarg.h>
#include <stdio.h>
+#include <sys/mount.h>
#include <sys/stat.h>
#include <android-base/properties.h>
@@ -37,12 +38,9 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_unique_fd.h"
-#include "remount_service.h"
#include "fec/io.h"
-struct fstab *fstab;
-
#ifdef ALLOW_ADBD_DISABLE_VERITY
static const bool kAllowDisableVerity = true;
#else
@@ -53,6 +51,17 @@
if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n");
}
+static bool make_block_device_writable(const std::string& dev) {
+ unique_fd fd(unix_open(dev, O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return false;
+ }
+
+ int OFF = 0;
+ bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
+ return result;
+}
+
/* Turn verity on/off */
static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point,
bool enable) {
@@ -102,8 +111,11 @@
WriteFdFmt(fd, "%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
}
} else if (errno) {
- WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
- mount_point, strerror(errno));
+ int expected_errno = enable ? EBUSY : ENOENT;
+ if (errno != expected_errno) {
+ WriteFdFmt(fd, "Overlayfs %s for %s failed with error %s\n",
+ enable ? "teardown" : "setup", mount_point, strerror(errno));
+ }
}
WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
return true;
@@ -185,7 +197,7 @@
}
if (!android::base::GetBoolProperty("ro.secure", false)) {
- overlayfs_setup(fd, enable);
+ overlayfs_setup(fd.get(), enable);
WriteFdExactly(fd.get(), "verity not enabled - ENG build\n");
return;
}
@@ -213,24 +225,24 @@
// Not using AVB - assume VB1.0.
// read all fstab entries at once from all sources
- if (!fstab) fstab = fs_mgr_read_fstab_default();
- if (!fstab) {
+ android::fs_mgr::Fstab fstab;
+ if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
WriteFdExactly(fd.get(), "Failed to read fstab\n");
suggest_run_adb_root(fd.get());
return;
}
// Loop through entries looking for ones that verity manages.
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fs_mgr_is_verified(&fstab->recs[i])) {
- if (set_verity_enabled_state(fd.get(), fstab->recs[i].blk_device,
- fstab->recs[i].mount_point, enable)) {
+ for (const auto& entry : fstab) {
+ if (entry.fs_mgr_flags.verify) {
+ if (set_verity_enabled_state(fd.get(), entry.blk_device.c_str(),
+ entry.mount_point.c_str(), enable)) {
any_changed = true;
}
}
}
}
- if (!any_changed) any_changed = overlayfs_setup(fd, enable);
+ if (!any_changed) any_changed = overlayfs_setup(fd.get(), enable);
if (any_changed) {
WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
diff --git a/adb/daemon/set_verity_enable_state_service.h b/adb/daemon/set_verity_enable_state_service.h
index c1413c8..c0ed98e 100644
--- a/adb/daemon/set_verity_enable_state_service.h
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -18,4 +18,6 @@
#include "adb_unique_fd.h"
+#if defined(__ANDROID__)
void set_verity_enabled_state_service(unique_fd fd, bool enable);
+#endif
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index 01097ac..0fb14c4 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -98,7 +98,10 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/android_logger.h>
+
+#if defined(__ANDROID__)
#include <selinux/android.h>
+#endif
#include "adb.h"
#include "adb_io.h"
@@ -111,7 +114,7 @@
namespace {
// Reads from |fd| until close or failure.
-std::string ReadAll(int fd) {
+std::string ReadAll(borrowed_fd fd) {
char buffer[512];
std::string received;
@@ -140,8 +143,8 @@
class Subprocess {
public:
- Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol);
+ Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw);
~Subprocess();
const std::string& command() const { return command_; }
@@ -154,6 +157,10 @@
// and exec's the child. Returns false and sets error on failure.
bool ForkAndExec(std::string* _Nonnull error);
+ // Sets up FDs, starts a thread executing command and the manager thread,
+ // Returns false and sets error on failure.
+ bool ExecInProcess(Command command, std::string* _Nonnull error);
+
// Start the subprocess manager thread. Consumes the subprocess, regardless of success.
// Returns false and sets error on failure.
static bool StartThread(std::unique_ptr<Subprocess> subprocess,
@@ -163,6 +170,8 @@
// Opens the file at |pts_name|.
int OpenPtyChildFd(const char* pts_name, unique_fd* error_sfd);
+ bool ConnectProtocolEndpoints(std::string* _Nonnull error);
+
static void ThreadHandler(void* userdata);
void PassDataStreams();
void WaitForExit();
@@ -177,9 +186,9 @@
const std::string command_;
const std::string terminal_type_;
- bool make_pty_raw_ = false;
SubprocessType type_;
SubprocessProtocol protocol_;
+ bool make_pty_raw_;
pid_t pid_ = -1;
unique_fd local_socket_sfd_;
@@ -191,25 +200,13 @@
DISALLOW_COPY_AND_ASSIGN(Subprocess);
};
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol)
- : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw)
+ : command_(std::move(command)),
terminal_type_(terminal_type ? terminal_type : ""),
type_(type),
- protocol_(protocol) {
- // If we aren't using the shell protocol we must allocate a PTY to properly close the
- // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
- // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
- // e.g. screenrecord, will never notice the broken pipe and terminate.
- // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
- // with select() and will send SIGHUP manually to the child process.
- if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
- // Disable PTY input/output processing since the client is expecting raw data.
- D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
- type_ = SubprocessType::kPty;
- make_pty_raw_ = true;
- }
-}
+ protocol_(protocol),
+ make_pty_raw_(make_pty_raw) {}
Subprocess::~Subprocess() {
WaitForExit();
@@ -225,7 +222,7 @@
bool Subprocess::ForkAndExec(std::string* error) {
unique_fd child_stdinout_sfd, child_stderr_sfd;
unique_fd parent_error_sfd, child_error_sfd;
- char pts_name[PATH_MAX];
+ const char* pts_name = nullptr;
if (command_.empty()) {
__android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
@@ -273,7 +270,7 @@
}
std::vector<std::string> joined_env;
- for (auto it : env) {
+ for (const auto& it : env) {
const char* key = it.first.c_str();
const char* value = it.second.c_str();
joined_env.push_back(android::base::StringPrintf("%s=%s", key, value));
@@ -286,10 +283,22 @@
cenv.push_back(nullptr);
if (type_ == SubprocessType::kPty) {
- int fd;
- pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+ unique_fd pty_master(posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC));
+ if (pty_master == -1) {
+ *error =
+ android::base::StringPrintf("failed to create pty master: %s", strerror(errno));
+ return false;
+ }
+ if (unlockpt(pty_master.get()) != 0) {
+ *error = android::base::StringPrintf("failed to unlockpt pty master: %s",
+ strerror(errno));
+ return false;
+ }
+
+ pid_ = fork();
+ pts_name = ptsname(pty_master.get());
if (pid_ > 0) {
- stdinout_sfd_.reset(fd);
+ stdinout_sfd_ = std::move(pty_master);
}
} else {
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
@@ -320,9 +329,10 @@
child_stdinout_sfd.reset(OpenPtyChildFd(pts_name, &child_error_sfd));
}
- dup2(child_stdinout_sfd, STDIN_FILENO);
- dup2(child_stdinout_sfd, STDOUT_FILENO);
- dup2(child_stderr_sfd != -1 ? child_stderr_sfd : child_stdinout_sfd, STDERR_FILENO);
+ dup2(child_stdinout_sfd.get(), STDIN_FILENO);
+ dup2(child_stdinout_sfd.get(), STDOUT_FILENO);
+ dup2(child_stderr_sfd != -1 ? child_stderr_sfd.get() : child_stdinout_sfd.get(),
+ STDERR_FILENO);
// exec doesn't trigger destructors, close the FDs manually.
stdinout_sfd_.reset(-1);
@@ -388,16 +398,65 @@
}
D("subprocess parent: exec completed");
+ if (!ConnectProtocolEndpoints(error)) {
+ kill(pid_, SIGKILL);
+ return false;
+ }
+
+ D("subprocess parent: completed");
+ return true;
+}
+
+bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) {
+ unique_fd child_stdinout_sfd, child_stderr_sfd;
+
+ CHECK(type_ == SubprocessType::kRaw);
+
+ __android_log_security_bswrite(SEC_TAG_ADB_SHELL_CMD, command_.c_str());
+
+ if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
+ strerror(errno));
+ return false;
+ }
+ if (protocol_ == SubprocessProtocol::kShell) {
+ // Shell protocol allows for splitting stderr.
+ if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+ *error = android::base::StringPrintf("failed to create socketpair for stderr: %s",
+ strerror(errno));
+ return false;
+ }
+ } else {
+ // Raw protocol doesn't support multiple output streams, so combine stdout and stderr.
+ child_stderr_sfd.reset(dup(child_stdinout_sfd.get()));
+ }
+
+ D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
+ stderr_sfd_.get());
+
+ if (!ConnectProtocolEndpoints(error)) {
+ return false;
+ }
+
+ std::thread([inout_sfd = std::move(child_stdinout_sfd), err_sfd = std::move(child_stderr_sfd),
+ command = std::move(command),
+ args = command_]() { command(args, inout_sfd, inout_sfd, err_sfd); })
+ .detach();
+
+ D("execinprocess: completed");
+ return true;
+}
+
+bool Subprocess::ConnectProtocolEndpoints(std::string* _Nonnull error) {
if (protocol_ == SubprocessProtocol::kNone) {
// No protocol: all streams pass through the stdinout FD and hook
// directly into the local socket for raw data transfer.
local_socket_sfd_.reset(stdinout_sfd_.release());
} else {
- // Shell protocol: create another socketpair to intercept data.
+ // Required for shell protocol: create another socketpair to intercept data.
if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
*error = android::base::StringPrintf(
- "failed to create socketpair to intercept data: %s", strerror(errno));
- kill(pid_, SIGKILL);
+ "failed to create socketpair to intercept data: %s", strerror(errno));
return false;
}
D("protocol FD = %d", protocol_sfd_.get());
@@ -406,7 +465,6 @@
output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
if (!input_ || !output_) {
*error = "failed to allocate shell protocol objects";
- kill(pid_, SIGKILL);
return false;
}
@@ -418,15 +476,13 @@
if (fd >= 0) {
if (!set_file_block_mode(fd, false)) {
*error = android::base::StringPrintf(
- "failed to set non-blocking mode for fd %d", fd);
- kill(pid_, SIGKILL);
+ "failed to set non-blocking mode for fd %d", fd);
return false;
}
}
}
}
- D("subprocess parent: completed");
return true;
}
@@ -494,7 +550,7 @@
FD_ZERO(&master_write_set);
for (unique_fd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
if (*sfd != -1) {
- FD_SET(*sfd, &master_read_set);
+ FD_SET(sfd->get(), &master_read_set);
}
}
@@ -504,15 +560,17 @@
unique_fd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
if (dead_sfd) {
D("closing FD %d", dead_sfd->get());
- FD_CLR(*dead_sfd, &master_read_set);
- FD_CLR(*dead_sfd, &master_write_set);
+ FD_CLR(dead_sfd->get(), &master_read_set);
+ FD_CLR(dead_sfd->get(), &master_write_set);
if (dead_sfd == &protocol_sfd_) {
// Using SIGHUP is a decent general way to indicate that the
// controlling process is going away. If specific signals are
// needed (e.g. SIGINT), pass those through the shell protocol
// and only fall back on this for unexpected closures.
D("protocol FD died, sending SIGHUP to pid %d", pid_);
- kill(pid_, SIGHUP);
+ if (pid_ != -1) {
+ kill(pid_, SIGHUP);
+ }
// We also need to close the pipes connected to the child process
// so that if it ignores SIGHUP and continues to write data it
@@ -528,7 +586,7 @@
namespace {
inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
- return sfd != -1 && FD_ISSET(sfd, set);
+ return sfd != -1 && FD_ISSET(sfd.get(), set);
}
} // namespace
@@ -536,7 +594,8 @@
unique_fd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
fd_set* master_write_set_ptr) {
fd_set read_set, write_set;
- int select_n = std::max(std::max(protocol_sfd_, stdinout_sfd_), stderr_sfd_) + 1;
+ int select_n =
+ std::max(std::max(protocol_sfd_.get(), stdinout_sfd_.get()), stderr_sfd_.get()) + 1;
unique_fd* dead_sfd = nullptr;
// Keep calling select() and passing data until an FD closes/errors.
@@ -569,8 +628,8 @@
dead_sfd = PassInput();
// If we didn't finish writing, block on stdin write.
if (input_bytes_left_) {
- FD_CLR(protocol_sfd_, master_read_set_ptr);
- FD_SET(stdinout_sfd_, master_write_set_ptr);
+ FD_CLR(protocol_sfd_.get(), master_read_set_ptr);
+ FD_SET(stdinout_sfd_.get(), master_write_set_ptr);
}
}
@@ -579,8 +638,8 @@
dead_sfd = PassInput();
// If we finished writing, go back to blocking on protocol read.
if (!input_bytes_left_) {
- FD_SET(protocol_sfd_, master_read_set_ptr);
- FD_CLR(stdinout_sfd_, master_write_set_ptr);
+ FD_SET(protocol_sfd_.get(), master_read_set_ptr);
+ FD_CLR(stdinout_sfd_.get(), master_write_set_ptr);
}
}
} // while (!dead_sfd)
@@ -594,7 +653,7 @@
if (!input_->Read()) {
// Read() uses ReadFdExactly() which sets errno to 0 on EOF.
if (errno != 0) {
- PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.get();
}
return &protocol_sfd_;
}
@@ -610,7 +669,7 @@
ws.ws_col = cols;
ws.ws_xpixel = x_pixels;
ws.ws_ypixel = y_pixels;
- ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
+ ioctl(stdinout_sfd_.get(), TIOCSWINSZ, &ws);
}
break;
case ShellProtocol::kIdStdin:
@@ -621,8 +680,7 @@
if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
return nullptr;
}
- PLOG(ERROR) << "failed to shutdown writes to FD "
- << stdinout_sfd_;
+ PLOG(ERROR) << "failed to shutdown writes to FD " << stdinout_sfd_.get();
return &stdinout_sfd_;
} else {
// PTYs can't close just input, so rather than close the
@@ -643,7 +701,7 @@
int bytes = adb_write(stdinout_sfd_, input_->data() + index, input_bytes_left_);
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
if (bytes < 0) {
- PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_;
+ PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_.get();
}
// stdin is done, mark this packet as finished and we'll just start
// dumping any further data received from the protocol FD.
@@ -663,14 +721,14 @@
// read() returns EIO if a PTY closes; don't report this as an error,
// it just means the subprocess completed.
if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
- PLOG(ERROR) << "error reading output FD " << *sfd;
+ PLOG(ERROR) << "error reading output FD " << sfd->get();
}
return sfd;
}
if (bytes > 0 && !output_->Write(id, bytes)) {
if (errno != 0) {
- PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
+ PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.get();
}
return &protocol_sfd_;
}
@@ -682,7 +740,7 @@
int exit_code = 1;
D("waiting for pid %d", pid_);
- while (true) {
+ while (pid_ != -1) {
int status;
if (pid_ == waitpid(pid_, &status, 0)) {
D("post waitpid (pid=%d) status=%04x", pid_, status);
@@ -716,7 +774,7 @@
} // namespace
// Create a pipe containing the error.
-static unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message) {
unique_fd read, write;
if (!Pipe(&read, &write)) {
PLOG(ERROR) << "failed to create pipe to report error";
@@ -745,23 +803,51 @@
return read;
}
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol) {
+ // If we aren't using the shell protocol we must allocate a PTY to properly close the
+ // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+ // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+ // e.g. screenrecord, will never notice the broken pipe and terminate.
+ // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+ // with select() and will send SIGHUP manually to the child process.
+ bool make_pty_raw = false;
+ if (protocol == SubprocessProtocol::kNone && type == SubprocessType::kRaw) {
+ // Disable PTY input/output processing since the client is expecting raw data.
+ D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+ type = SubprocessType::kPty;
+ make_pty_raw = true;
+ }
+
+ unique_fd error_fd;
+ unique_fd fd = StartSubprocess(std::move(name), terminal_type, type, protocol, make_pty_raw,
+ protocol, &error_fd);
+ if (fd == -1) {
+ return error_fd;
+ }
+ return fd;
+}
+
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw,
+ SubprocessProtocol error_protocol, unique_fd* error_fd) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
- protocol == SubprocessProtocol::kNone ? "none" : "shell",
- terminal_type, name);
+ protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
- auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+ make_pty_raw);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
- return ReportError(protocol, "failed to allocate new subprocess");
+ *error_fd = ReportError(error_protocol, "failed to allocate new subprocess");
+ return {};
}
std::string error;
if (!subprocess->ForkAndExec(&error)) {
LOG(ERROR) << "failed to start subprocess: " << error;
- return ReportError(protocol, error);
+ *error_fd = ReportError(error_protocol, error);
+ return {};
}
unique_fd local_socket(subprocess->ReleaseLocalSocket());
@@ -770,6 +856,39 @@
if (!Subprocess::StartThread(std::move(subprocess), &error)) {
LOG(ERROR) << "failed to start subprocess management thread: " << error;
+ *error_fd = ReportError(error_protocol, error);
+ return {};
+ }
+
+ return local_socket;
+}
+
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol) {
+ LOG(INFO) << "StartCommandInProcess(" << dump_hex(name.data(), name.size()) << ")";
+
+ constexpr auto terminal_type = "";
+ constexpr auto type = SubprocessType::kRaw;
+ constexpr auto make_pty_raw = false;
+
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol,
+ make_pty_raw);
+ if (!subprocess) {
+ LOG(ERROR) << "failed to allocate new subprocess";
+ return ReportError(protocol, "failed to allocate new subprocess");
+ }
+
+ std::string error;
+ if (!subprocess->ExecInProcess(std::move(command), &error)) {
+ LOG(ERROR) << "failed to start subprocess: " << error;
+ return ReportError(protocol, error);
+ }
+
+ unique_fd local_socket(subprocess->ReleaseLocalSocket());
+ D("inprocess creation successful: local_socket_fd=%d, pid=%d", local_socket.get(),
+ subprocess->pid());
+
+ if (!Subprocess::StartThread(std::move(subprocess), &error)) {
+ LOG(ERROR) << "failed to start inprocess management thread: " << error;
return ReportError(protocol, error);
}
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 2a48923..030228c 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -16,8 +16,12 @@
#pragma once
+#include <string>
+
#include "adb_unique_fd.h"
+#include <string_view>
+
enum class SubprocessType {
kPty,
kRaw,
@@ -32,5 +36,20 @@
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol);
+
+// The same as above but with more fined grained control and custom error handling.
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol, bool make_pty_raw,
+ SubprocessProtocol error_protocol, unique_fd* error_fd);
+
+// Executes |command| in a separate thread.
+// Sets up in/out and error streams to emulate shell-like behavior.
+//
+// Returns an open FD connected to the thread or -1 on failure.
+using Command = int(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err);
+unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
+
+// Create a pipe containing the error.
+unique_fd ReportError(SubprocessProtocol protocol, const std::string& message);
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index 323bcec..cdd8dbe 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -35,7 +35,6 @@
static void SetUpTestCase() {
// This is normally done in main.cpp.
saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
-
}
static void TearDownTestCase() {
@@ -49,30 +48,36 @@
SubprocessProtocol protocol);
void CleanupTestSubprocess();
- virtual void TearDown() override {
- void CleanupTestSubprocess();
- }
+ void StartTestCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
+
+ virtual void TearDown() override { CleanupTestSubprocess(); }
static sighandler_t saved_sigpipe_handler_;
- unique_fd subprocess_fd_;
+ unique_fd command_fd_;
};
sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
void ShellServiceTest::StartTestSubprocess(
const char* command, SubprocessType type, SubprocessProtocol protocol) {
- subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
- ASSERT_TRUE(subprocess_fd_ >= 0);
+ command_fd_ = StartSubprocess(command, nullptr, type, protocol);
+ ASSERT_TRUE(command_fd_ >= 0);
}
void ShellServiceTest::CleanupTestSubprocess() {
}
+void ShellServiceTest::StartTestCommandInProcess(std::string name, Command command,
+ SubprocessProtocol protocol) {
+ command_fd_ = StartCommandInProcess(std::move(name), std::move(command), protocol);
+ ASSERT_TRUE(command_fd_ >= 0);
+}
+
namespace {
// Reads raw data from |fd| until it closes or errors.
-std::string ReadRaw(int fd) {
+std::string ReadRaw(borrowed_fd fd) {
char buffer[1024];
char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
@@ -88,12 +93,12 @@
// Reads shell protocol data from |fd| until it closes or errors. Fills
// |stdout| and |stderr| with their respective data, and returns the exit code
// read from the protocol or -1 if an exit code packet was not received.
-int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
+int ReadShellProtocol(borrowed_fd fd, std::string* stdout, std::string* stderr) {
int exit_code = -1;
stdout->clear();
stderr->clear();
- ShellProtocol* protocol = new ShellProtocol(fd);
+ auto protocol = std::make_unique<ShellProtocol>(fd.get());
while (protocol->Read()) {
switch (protocol->id()) {
case ShellProtocol::kIdStdout:
@@ -111,7 +116,6 @@
ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
}
}
- delete protocol;
return exit_code;
}
@@ -154,7 +158,7 @@
// [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without
// the shell protocol we should always force a PTY to ensure proper cleanup.
- ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+ ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"});
}
// Tests a PTY subprocess with no protocol.
@@ -165,7 +169,7 @@
SubprocessType::kPty, SubprocessProtocol::kNone));
// [ -t 0 ] == 0 means we have a terminal (PTY).
- ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+ ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"});
}
// Tests a raw subprocess with the shell protocol.
@@ -175,7 +179,7 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(24, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "baz"});
ExpectLinesEqual(stderr, {"bar"});
}
@@ -189,7 +193,7 @@
// PTY always combines stdout and stderr but the shell protocol should
// still give us an exit code.
std::string stdout, stderr;
- EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(50, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
ExpectLinesEqual(stderr, {});
}
@@ -204,7 +208,7 @@
"echo --${TEST_STR}--",
"exit"};
- ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ ShellProtocol* protocol = new ShellProtocol(command_fd_);
for (std::string command : commands) {
// Interactive shell requires a newline to complete each command.
command.push_back('\n');
@@ -214,7 +218,7 @@
delete protocol;
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
// An unpredictable command prompt makes parsing exact output difficult but
// it should at least contain echoed input and the expected output.
for (const char* command : commands) {
@@ -230,14 +234,14 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string input = "foo\nbar";
- ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ ShellProtocol* protocol = new ShellProtocol(command_fd_);
memcpy(protocol->data(), input.data(), input.length());
ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
delete protocol;
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
ExpectLinesEqual(stderr, {});
}
@@ -249,7 +253,7 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {});
ExpectLinesEqual(stderr, {"bar"});
}
@@ -261,7 +265,56 @@
SubprocessType::kRaw, SubprocessProtocol::kShell));
std::string stdout, stderr;
- EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr));
ExpectLinesEqual(stdout, {"foo"});
ExpectLinesEqual(stderr, {});
}
+
+// Tests an inprocess command with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolInprocess) {
+ ASSERT_NO_FATAL_FAILURE(
+ StartTestCommandInProcess("123",
+ [](auto args, auto in, auto out, auto err) -> int {
+ EXPECT_EQ("123", args);
+ char input[10];
+ EXPECT_TRUE(ReadFdExactly(in, input, 2));
+ input[2] = 0;
+ EXPECT_STREQ("in", input);
+ WriteFdExactly(out, "out\n");
+ WriteFdExactly(err, "err\n");
+ return 0;
+ },
+ SubprocessProtocol::kNone));
+
+ WriteFdExactly(command_fd_, "in");
+ ExpectLinesEqual(ReadRaw(command_fd_), {"out", "err"});
+}
+
+// Tests an inprocess command with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolInprocess) {
+ ASSERT_NO_FATAL_FAILURE(
+ StartTestCommandInProcess("321",
+ [](auto args, auto in, auto out, auto err) -> int {
+ EXPECT_EQ("321", args);
+ char input[10];
+ EXPECT_TRUE(ReadFdExactly(in, input, 2));
+ input[2] = 0;
+ EXPECT_STREQ("in", input);
+ WriteFdExactly(out, "out\n");
+ WriteFdExactly(err, "err\n");
+ return 0;
+ },
+ SubprocessProtocol::kShell));
+
+ {
+ auto write_protocol = std::make_unique<ShellProtocol>(command_fd_);
+ memcpy(write_protocol->data(), "in", 2);
+ write_protocol->Write(ShellProtocol::kIdStdin, 2);
+ }
+
+ std::string stdout, stderr;
+ // For in-process commands the exit code is always the default (1).
+ EXPECT_EQ(1, ReadShellProtocol(command_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"out"});
+ ExpectLinesEqual(stderr, {"err"});
+}
diff --git a/adb/daemon/transport_qemu.cpp b/adb/daemon/transport_qemu.cpp
new file mode 100644
index 0000000..aa760bc
--- /dev/null
+++ b/adb/daemon/transport_qemu.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Include qemu_pipe.h before sysdeps, since it has inlined references to open, read, write.
+#include <qemu_pipe.h>
+
+#define TRACE_TAG TRANSPORT
+#include "sysdeps.h"
+#include "transport.h"
+
+#include <android-base/properties.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+
+/* A worker thread that monitors host connections, and registers a transport for
+ * every new host connection. This thread replaces server_socket_thread on
+ * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
+ * pipe to communicate with adbd daemon inside the guest. This is done in order
+ * to provide more robust communication channel between ADB host and guest. The
+ * main issue with server_socket_thread approach is that it runs on top of TCP,
+ * and thus is sensitive to network disruptions. For instance, the
+ * ConnectionManager may decide to reset all network connections, in which case
+ * the connection between ADB host and guest will be lost. To make ADB traffic
+ * independent from the network, we use here 'adb' QEMUD service to transfer data
+ * between the host, and the guest. See external/qemu/android/adb-*.* that
+ * implements the emulator's side of the protocol. Another advantage of using
+ * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
+ * anymore on network being set up.
+ * The guest side of the protocol contains the following phases:
+ * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
+ * is opened, and it becomes clear whether or not emulator supports that
+ * protocol.
+ * - Wait for the ADB host to create connection with the guest. This is done by
+ * sending an 'accept' request to the adb QEMUD service, and waiting on
+ * response.
+ * - When new ADB host connection is accepted, the connection with adb QEMUD
+ * service is registered as the transport, and a 'start' request is sent to the
+ * adb QEMUD service, indicating that the guest is ready to receive messages.
+ * Note that the guest will ignore messages sent down from the emulator before
+ * the transport registration is completed. That's why we need to send the
+ * 'start' request after the transport is registered.
+ */
+void qemu_socket_thread(int port) {
+ /* 'accept' request to the adb QEMUD service. */
+ static const char _accept_req[] = "accept";
+ /* 'start' request to the adb QEMUD service. */
+ static const char _start_req[] = "start";
+ /* 'ok' reply from the adb QEMUD service. */
+ static const char _ok_resp[] = "ok";
+
+ char tmp[256];
+ char con_name[32];
+
+ adb_thread_setname("qemu socket");
+ D("transport: qemu_socket_thread() starting");
+
+ /* adb QEMUD service connection request. */
+ snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
+
+ /* Connect to the adb QEMUD service. */
+ unique_fd fd(qemu_pipe_open(con_name));
+ if (fd < 0) {
+ /* This could be an older version of the emulator, that doesn't
+ * implement adb QEMUD service. Fall back to the old TCP way. */
+ D("adb service is not available. Falling back to TCP socket.");
+ std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+ return;
+ }
+
+ while (true) {
+ /*
+ * Wait till the host creates a new connection.
+ */
+
+ /* Send the 'accept' request. */
+ if (WriteFdExactly(fd.get(), _accept_req, strlen(_accept_req))) {
+ /* Wait for the response. In the response we expect 'ok' on success,
+ * or 'ko' on failure. */
+ if (!ReadFdExactly(fd.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
+ D("Accepting ADB host connection has failed.");
+ } else {
+ /* Host is connected. Register the transport, and start the
+ * exchange. */
+ std::string serial = android::base::StringPrintf("host-%d", fd.get());
+ WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
+ register_socket_transport(std::move(fd), std::move(serial), port, 1,
+ [](atransport*) { return ReconnectResult::Abort; });
+ }
+
+ /* Prepare for accepting of the next ADB host connection. */
+ fd.reset(qemu_pipe_open(con_name));
+ if (fd < 0) {
+ D("adb service become unavailable.");
+ return;
+ }
+ } else {
+ D("Unable to send the '%s' request to ADB service.", _accept_req);
+ return;
+ }
+ }
+ D("transport: qemu_socket_thread() exiting");
+ return;
+}
+
+// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
+// goldfish) as the transport. This can either be explicitly set by the
+// service.adb.transport property, or be inferred from ro.kernel.qemu that is
+// set to "1" for ranchu/goldfish.
+bool use_qemu_goldfish() {
+ // Legacy way to detect if adbd should use the goldfish pipe is to check for
+ // ro.kernel.qemu, keep that behaviour for backward compatibility.
+ if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+ return true;
+ }
+ // If service.adb.transport is present and is set to "goldfish", use the
+ // QEMUD pipe.
+ if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
+ return true;
+ }
+ return false;
+}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index f603d13..f4aa9fb 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -29,6 +29,7 @@
#include <linux/usb/functionfs.h>
#include <sys/eventfd.h>
+#include <algorithm>
#include <array>
#include <future>
#include <memory>
@@ -53,10 +54,17 @@
using android::base::StringPrintf;
-static constexpr size_t kUsbReadQueueDepth = 16;
-static constexpr size_t kUsbReadSize = 16384;
+// We can't find out whether we have support for AIO on ffs endpoints until we submit a read.
+static std::optional<bool> gFfsAioSupported;
-static constexpr size_t kUsbWriteQueueDepth = 16;
+// Not all USB controllers support operations larger than 16k, so don't go above that.
+// Also, each submitted operation does an allocation in the kernel of that size, so we want to
+// minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed.
+static constexpr size_t kUsbReadQueueDepth = 8;
+static constexpr size_t kUsbReadSize = 4 * PAGE_SIZE;
+
+static constexpr size_t kUsbWriteQueueDepth = 8;
+static constexpr size_t kUsbWriteSize = 4 * PAGE_SIZE;
static const char* to_string(enum usb_functionfs_event_type type) {
switch (type) {
@@ -110,9 +118,9 @@
};
struct IoBlock {
- bool pending;
- struct iocb control;
- Block payload;
+ bool pending = false;
+ struct iocb control = {};
+ std::shared_ptr<Block> payload;
TransferId id() const { return TransferId::from_value(control.aio_data); }
};
@@ -163,14 +171,20 @@
struct UsbFfsConnection : public Connection {
UsbFfsConnection(unique_fd control, unique_fd read, unique_fd write,
std::promise<void> destruction_notifier)
- : stopped_(false),
+ : worker_started_(false),
+ stopped_(false),
destruction_notifier_(std::move(destruction_notifier)),
control_fd_(std::move(control)),
read_fd_(std::move(read)),
write_fd_(std::move(write)) {
LOG(INFO) << "UsbFfsConnection constructed";
- event_fd_.reset(eventfd(0, EFD_CLOEXEC));
- if (event_fd_ == -1) {
+ worker_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (worker_event_fd_ == -1) {
+ PLOG(FATAL) << "failed to create eventfd";
+ }
+
+ monitor_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+ if (monitor_event_fd_ == -1) {
PLOG(FATAL) << "failed to create eventfd";
}
@@ -181,6 +195,14 @@
LOG(INFO) << "UsbFfsConnection being destroyed";
Stop();
monitor_thread_.join();
+
+ // We need to explicitly close our file descriptors before we notify our destruction,
+ // because the thread listening on the future will immediately try to reopen the endpoint.
+ aio_context_.reset();
+ control_fd_.reset();
+ read_fd_.reset();
+ write_fd_.reset();
+
destruction_notifier_.set_value();
}
@@ -192,8 +214,20 @@
std::lock_guard<std::mutex> lock(write_mutex_);
write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
if (!packet->payload.empty()) {
- write_requests_.push_back(
- CreateWriteBlock(std::move(packet->payload), next_write_id_++));
+ // The kernel attempts to allocate a contiguous block of memory for each write,
+ // which can fail if the write is large and the kernel heap is fragmented.
+ // Split large writes into smaller chunks to avoid this.
+ std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+ size_t offset = 0;
+ size_t len = payload->size();
+
+ while (len > 0) {
+ size_t write_size = std::min(kUsbWriteSize, len);
+ write_requests_.push_back(
+ CreateWriteBlock(payload, offset, write_size, next_write_id_++));
+ len -= write_size;
+ offset += write_size;
+ }
}
SubmitWrites();
return true;
@@ -207,11 +241,18 @@
}
stopped_ = true;
uint64_t notify = 1;
- ssize_t rc = adb_write(event_fd_.get(), ¬ify, sizeof(notify));
+ ssize_t rc = adb_write(worker_event_fd_.get(), ¬ify, sizeof(notify));
if (rc < 0) {
- PLOG(FATAL) << "failed to notify eventfd to stop UsbFfsConnection";
+ PLOG(FATAL) << "failed to notify worker eventfd to stop UsbFfsConnection";
}
CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
+
+ rc = adb_write(monitor_event_fd_.get(), ¬ify, sizeof(notify));
+ if (rc < 0) {
+ PLOG(FATAL) << "failed to notify monitor eventfd to stop UsbFfsConnection";
+ }
+
+ CHECK_EQ(static_cast<size_t>(rc), sizeof(notify));
}
private:
@@ -224,7 +265,6 @@
// until it dies, and then report failure to the transport via HandleError, which will
// eventually result in the transport being destroyed, which will result in UsbFfsConnection
// being destroyed, which unblocks the open thread and restarts this entire process.
- static constexpr int kInterruptionSignal = SIGUSR1;
static std::once_flag handler_once;
std::call_once(handler_once, []() { signal(kInterruptionSignal, [](int) {}); });
@@ -232,31 +272,40 @@
adb_thread_setname("UsbFfs-monitor");
bool bound = false;
- bool started = false;
+ bool enabled = false;
bool running = true;
while (running) {
- if (!bound || !started) {
- adb_pollfd pfd = {.fd = control_fd_.get(), .events = POLLIN, .revents = 0};
- int rc = TEMP_FAILURE_RETRY(adb_poll(&pfd, 1, 5000 /*ms*/));
- if (rc == -1) {
- PLOG(FATAL) << "poll on USB control fd failed";
- } else if (rc == 0) {
- // Something in the kernel presumably went wrong.
- // Close our endpoints, wait for a bit, and then try again.
- aio_context_.reset();
- read_fd_.reset();
- write_fd_.reset();
- control_fd_.reset();
- std::this_thread::sleep_for(5s);
- HandleError("didn't receive FUNCTIONFS_ENABLE, retrying");
- return;
- }
+ adb_pollfd pfd[2] = {
+ { .fd = control_fd_.get(), .events = POLLIN, .revents = 0 },
+ { .fd = monitor_event_fd_.get(), .events = POLLIN, .revents = 0 },
+ };
+
+ // If we don't see our first bind within a second, try again.
+ int timeout_ms = bound ? -1 : 1000;
+
+ int rc = TEMP_FAILURE_RETRY(adb_poll(pfd, 2, timeout_ms));
+ if (rc == -1) {
+ PLOG(FATAL) << "poll on USB control fd failed";
+ } else if (rc == 0) {
+ LOG(WARNING) << "timed out while waiting for FUNCTIONFS_BIND, trying again";
+ break;
+ }
+
+ if (pfd[1].revents) {
+ // We were told to die.
+ break;
}
struct usb_functionfs_event event;
- if (TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event))) !=
- sizeof(event)) {
+ rc = TEMP_FAILURE_RETRY(adb_read(control_fd_.get(), &event, sizeof(event)));
+ if (rc == -1) {
PLOG(FATAL) << "failed to read functionfs event";
+ } else if (rc == 0) {
+ LOG(WARNING) << "hit EOF on functionfs control fd";
+ break;
+ } else if (rc != sizeof(event)) {
+ LOG(FATAL) << "read functionfs event of unexpected size, expected "
+ << sizeof(event) << ", got " << rc;
}
LOG(INFO) << "USB event: "
@@ -264,78 +313,163 @@
switch (event.type) {
case FUNCTIONFS_BIND:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already bound?";
+ if (bound) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+ running = false;
+ break;
+ }
+
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+ running = false;
+ break;
+ }
+
bound = true;
break;
case FUNCTIONFS_ENABLE:
- CHECK(!started) << "received FUNCTIONFS_ENABLE while already running?";
- started = true;
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+ running = false;
+ break;
+ }
+
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
+ running = false;
+ break;
+ }
+
+ enabled = true;
StartWorker();
break;
case FUNCTIONFS_DISABLE:
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+ }
+
+ if (!enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
+ }
+
+ enabled = false;
running = false;
break;
+
+ case FUNCTIONFS_UNBIND:
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
+ }
+
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+ }
+
+ bound = false;
+ running = false;
+ break;
+
+ case FUNCTIONFS_SETUP: {
+ LOG(INFO) << "received FUNCTIONFS_SETUP control transfer: bRequestType = "
+ << static_cast<int>(event.u.setup.bRequestType)
+ << ", bRequest = " << static_cast<int>(event.u.setup.bRequest)
+ << ", wValue = " << static_cast<int>(event.u.setup.wValue)
+ << ", wIndex = " << static_cast<int>(event.u.setup.wIndex)
+ << ", wLength = " << static_cast<int>(event.u.setup.wLength);
+
+ if ((event.u.setup.bRequestType & USB_DIR_IN)) {
+ LOG(INFO) << "acking device-to-host control transfer";
+ ssize_t rc = adb_write(control_fd_.get(), "", 0);
+ if (rc != 0) {
+ PLOG(ERROR) << "failed to write empty packet to host";
+ break;
+ }
+ } else {
+ std::string buf;
+ buf.resize(event.u.setup.wLength + 1);
+
+ ssize_t rc = adb_read(control_fd_.get(), buf.data(), buf.size());
+ if (rc != event.u.setup.wLength) {
+ LOG(ERROR)
+ << "read " << rc
+ << " bytes when trying to read control request, expected "
+ << event.u.setup.wLength;
+ }
+
+ LOG(INFO) << "control request contents: " << buf;
+ break;
+ }
+ }
}
}
- pthread_t worker_thread_handle = worker_thread_.native_handle();
- while (true) {
- int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
- if (rc != 0) {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- break;
- }
-
- std::this_thread::sleep_for(100ms);
-
- rc = pthread_kill(worker_thread_handle, 0);
- if (rc == 0) {
- continue;
- } else if (rc == ESRCH) {
- break;
- } else {
- LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
- }
- }
-
- worker_thread_.join();
-
- aio_context_.reset();
- read_fd_.reset();
- write_fd_.reset();
+ StopWorker();
+ HandleError("monitor thread finished");
});
}
void StartWorker() {
+ CHECK(!worker_started_);
+ worker_started_ = true;
worker_thread_ = std::thread([this]() {
adb_thread_setname("UsbFfs-worker");
for (size_t i = 0; i < kUsbReadQueueDepth; ++i) {
read_requests_[i] = CreateReadBlock(next_read_id_++);
- SubmitRead(&read_requests_[i]);
+ if (!SubmitRead(&read_requests_[i])) {
+ return;
+ }
}
while (!stopped_) {
uint64_t dummy;
- ssize_t rc = adb_read(event_fd_.get(), &dummy, sizeof(dummy));
+ ssize_t rc = adb_read(worker_event_fd_.get(), &dummy, sizeof(dummy));
if (rc == -1) {
PLOG(FATAL) << "failed to read from eventfd";
} else if (rc == 0) {
LOG(FATAL) << "hit EOF on eventfd";
}
- WaitForEvents();
+ ReadEvents();
}
});
}
+ void StopWorker() {
+ if (!worker_started_) {
+ return;
+ }
+
+ pthread_t worker_thread_handle = worker_thread_.native_handle();
+ while (true) {
+ int rc = pthread_kill(worker_thread_handle, kInterruptionSignal);
+ if (rc != 0) {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ break;
+ }
+
+ std::this_thread::sleep_for(100ms);
+
+ rc = pthread_kill(worker_thread_handle, 0);
+ if (rc == 0) {
+ continue;
+ } else if (rc == ESRCH) {
+ break;
+ } else {
+ LOG(ERROR) << "failed to send interruption signal to worker: " << strerror(rc);
+ }
+ }
+
+ worker_thread_.join();
+ }
+
void PrepareReadBlock(IoBlock* block, uint64_t id) {
block->pending = false;
- block->payload.resize(kUsbReadSize);
+ block->payload = std::make_shared<Block>(kUsbReadSize);
block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
+ block->control.aio_nbytes = block->payload->size();
}
IoBlock CreateReadBlock(uint64_t id) {
@@ -347,11 +481,11 @@
block.control.aio_fildes = read_fd_.get();
block.control.aio_offset = 0;
block.control.aio_flags = IOCB_FLAG_RESFD;
- block.control.aio_resfd = event_fd_.get();
+ block.control.aio_resfd = worker_event_fd_.get();
return block;
}
- void WaitForEvents() {
+ void ReadEvents() {
static constexpr size_t kMaxEvents = kUsbReadQueueDepth + kUsbWriteQueueDepth;
struct io_event events[kMaxEvents];
struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
@@ -375,24 +509,26 @@
}
if (id.direction == TransferDirection::READ) {
- HandleRead(id, event.res);
+ if (!HandleRead(id, event.res)) {
+ return;
+ }
} else {
HandleWrite(id);
}
}
}
- void HandleRead(TransferId id, int64_t size) {
+ bool HandleRead(TransferId id, int64_t size) {
uint64_t read_idx = id.id % kUsbReadQueueDepth;
IoBlock* block = &read_requests_[read_idx];
block->pending = false;
- block->payload.resize(size);
+ block->payload->resize(size);
// Notification for completed reads can be received out of order.
if (block->id().id != needed_read_id_) {
LOG(VERBOSE) << "read " << block->id().id << " completed while waiting for "
<< needed_read_id_;
- return;
+ return true;
}
for (uint64_t id = needed_read_id_;; ++id) {
@@ -401,23 +537,33 @@
if (current_block->pending) {
break;
}
- ProcessRead(current_block);
+ if (!ProcessRead(current_block)) {
+ return false;
+ }
++needed_read_id_;
}
+
+ return true;
}
- void ProcessRead(IoBlock* block) {
- if (!block->payload.empty()) {
+ bool ProcessRead(IoBlock* block) {
+ if (!block->payload->empty()) {
if (!incoming_header_.has_value()) {
- CHECK_EQ(sizeof(amessage), block->payload.size());
+ if (block->payload->size() != sizeof(amessage)) {
+ HandleError("received packet of unexpected length while reading header");
+ return false;
+ }
amessage msg;
- memcpy(&msg, block->payload.data(), sizeof(amessage));
+ memcpy(&msg, block->payload->data(), sizeof(amessage));
LOG(DEBUG) << "USB read:" << dump_header(&msg);
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(block->payload);
- CHECK_LE(payload.size(), bytes_left);
+ Block payload = std::move(*block->payload);
+ if (block->payload->size() > bytes_left) {
+ HandleError("received too many bytes while waiting for payload");
+ return false;
+ }
incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
}
@@ -436,15 +582,25 @@
PrepareReadBlock(block, block->id().id + kUsbReadQueueDepth);
SubmitRead(block);
+ return true;
}
- void SubmitRead(IoBlock* block) {
+ bool SubmitRead(IoBlock* block) {
block->pending = true;
struct iocb* iocb = &block->control;
if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
+ if (errno == EINVAL && !gFfsAioSupported.has_value()) {
+ HandleError("failed to submit first read, AIO on FFS not supported");
+ gFfsAioSupported = false;
+ return false;
+ }
+
HandleError(StringPrintf("failed to submit read: %s", strerror(errno)));
- return;
+ return false;
}
+
+ gFfsAioSupported = true;
+ return true;
}
void HandleWrite(TransferId id) {
@@ -462,7 +618,8 @@
SubmitWrites();
}
- std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
+ size_t len, uint64_t id) {
auto block = std::make_unique<IoBlock>();
block->payload = std::move(payload);
block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
@@ -470,14 +627,20 @@
block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
block->control.aio_reqprio = 0;
block->control.aio_fildes = write_fd_.get();
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
- block->control.aio_nbytes = block->payload.size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
+ block->control.aio_nbytes = len;
block->control.aio_offset = 0;
block->control.aio_flags = IOCB_FLAG_RESFD;
- block->control.aio_resfd = event_fd_.get();
+ block->control.aio_resfd = worker_event_fd_.get();
return block;
}
+ std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
+ std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
+ size_t len = block->size();
+ return CreateWriteBlock(std::move(block), 0, len, id);
+ }
+
void SubmitWrites() REQUIRES(write_mutex_) {
if (writes_submitted_ == kUsbWriteQueueDepth) {
return;
@@ -498,6 +661,8 @@
LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
}
+ writes_submitted_ += writes_to_submit;
+
int rc = io_submit(aio_context_.get(), writes_to_submit, iocbs);
if (rc == -1) {
HandleError(StringPrintf("failed to submit write requests: %s", strerror(errno)));
@@ -506,8 +671,6 @@
LOG(FATAL) << "failed to submit all writes: wanted to submit " << writes_to_submit
<< ", actually submitted " << rc;
}
-
- writes_submitted_ += rc;
}
void HandleError(const std::string& error) {
@@ -520,13 +683,16 @@
}
std::thread monitor_thread_;
+
+ bool worker_started_;
std::thread worker_thread_;
std::atomic<bool> stopped_;
std::promise<void> destruction_notifier_;
std::once_flag error_flag_;
- unique_fd event_fd_;
+ unique_fd worker_event_fd_;
+ unique_fd monitor_event_fd_;
ScopedAioContext aio_context_;
unique_fd control_fd_;
@@ -549,12 +715,21 @@
std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
+
+ static constexpr int kInterruptionSignal = SIGUSR1;
};
+void usb_init_legacy();
+
static void usb_ffs_open_thread() {
adb_thread_setname("usb ffs open");
while (true) {
+ if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) {
+ LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy";
+ return usb_init_legacy();
+ }
+
unique_fd control;
unique_fd bulk_out;
unique_fd bulk_in;
@@ -575,11 +750,14 @@
}
}
-void usb_init_legacy();
void usb_init() {
- if (!android::base::GetBoolProperty("persist.adb.nonblocking_ffs", false)) {
- usb_init_legacy();
- } else {
+ bool use_nonblocking = android::base::GetBoolProperty(
+ "persist.adb.nonblocking_ffs",
+ android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true));
+
+ if (use_nonblocking) {
std::thread(usb_ffs_open_thread).detach();
+ } else {
+ usb_init_legacy();
}
}
diff --git a/adb/daemon/usb_dummy.cpp b/adb/daemon/usb_dummy.cpp
new file mode 100644
index 0000000..c9bf797
--- /dev/null
+++ b/adb/daemon/usb_dummy.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <adbd/usb.h>
+
+#include <android-base/logging.h>
+
+int usb_write(usb_handle*, const void*, int) {
+ LOG(FATAL) << "unimplemented";
+ return -1;
+}
+
+int usb_read(usb_handle*, void*, int) {
+ LOG(FATAL) << "unimplemented";
+ return -1;
+}
+
+int usb_close(usb_handle*) {
+ LOG(FATAL) << "unimplemented";
+ return -1;
+}
+
+void usb_reset(usb_handle*) {
+ LOG(FATAL) << "unimplemented";
+}
+
+void usb_kick(usb_handle*) {
+ LOG(FATAL) << "unimplemented";
+}
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 07b4ba8..a64ce40 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -37,9 +37,12 @@
// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.
#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)
+#define USB_EXT_PROP_UNICODE 1
+
#define cpu_to_le16(x) htole16(x)
#define cpu_to_le32(x) htole32(x)
+// clang-format off
struct func_desc {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio source;
@@ -64,6 +67,34 @@
struct func_desc fs_descs, hs_descs;
} __attribute__((packed));
+template <size_t PropertyNameLength, size_t PropertyDataLength>
+struct usb_os_desc_ext_prop {
+ uint32_t dwSize = sizeof(*this);
+ uint32_t dwPropertyDataType = cpu_to_le32(USB_EXT_PROP_UNICODE);
+
+ // Property name and value are transmitted as UTF-16, but the kernel only
+ // accepts ASCII values and performs the conversion for us.
+ uint16_t wPropertyNameLength = cpu_to_le16(PropertyNameLength);
+ char bPropertyName[PropertyNameLength];
+
+ uint32_t dwPropertyDataLength = cpu_to_le32(PropertyDataLength);
+ char bProperty[PropertyDataLength];
+} __attribute__((packed));
+
+using usb_os_desc_guid_t = usb_os_desc_ext_prop<20, 39>;
+usb_os_desc_guid_t os_desc_guid = {
+ .bPropertyName = "DeviceInterfaceGUID",
+ .bProperty = "{64379D6C-D531-4BED-BBEC-5A16FC07D6BC}",
+};
+
+struct usb_ext_prop_values {
+ usb_os_desc_guid_t guid;
+} __attribute__((packed));
+
+usb_ext_prop_values os_prop_values = {
+ .guid = os_desc_guid,
+};
+
struct desc_v2 {
struct usb_functionfs_descs_head_v2 header;
// The rest of the structure depends on the flags in the header.
@@ -75,9 +106,10 @@
struct ss_func_desc ss_descs;
struct usb_os_desc_header os_header;
struct usb_ext_compat_desc os_desc;
+ struct usb_os_desc_header os_prop_header;
+ struct usb_ext_prop_values os_prop_values;
} __attribute__((packed));
-// clang-format off
static struct func_desc fs_descriptors = {
.intf = {
.bLength = sizeof(fs_descriptors.intf),
@@ -172,13 +204,13 @@
struct usb_ext_compat_desc os_desc_compat = {
.bFirstInterfaceNumber = 0,
.Reserved1 = cpu_to_le32(1),
- .CompatibleID = {0},
+ .CompatibleID = { 'W', 'I', 'N', 'U', 'S', 'B', '\0', '\0'},
.SubCompatibleID = {0},
.Reserved2 = {0},
};
static struct usb_os_desc_header os_desc_header = {
- .interface = cpu_to_le32(1),
+ .interface = cpu_to_le32(0),
.dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
.bcdVersion = cpu_to_le32(1),
.wIndex = cpu_to_le32(4),
@@ -186,6 +218,14 @@
.Reserved = cpu_to_le32(0),
};
+static struct usb_os_desc_header os_prop_header = {
+ .interface = cpu_to_le32(0),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_prop_values)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(5),
+ .wCount = cpu_to_le16(1),
+};
+
#define STR_INTERFACE_ "ADB Interface"
static const struct {
@@ -221,12 +261,14 @@
v2_descriptor.fs_count = 3;
v2_descriptor.hs_count = 3;
v2_descriptor.ss_count = 5;
- v2_descriptor.os_count = 1;
+ v2_descriptor.os_count = 2;
v2_descriptor.fs_descs = fs_descriptors;
v2_descriptor.hs_descs = hs_descriptors;
v2_descriptor.ss_descs = ss_descriptors;
v2_descriptor.os_header = os_desc_header;
v2_descriptor.os_desc = os_desc_compat;
+ v2_descriptor.os_prop_header = os_prop_header;
+ v2_descriptor.os_prop_values = os_prop_values;
if (out_control->get() < 0) { // might have already done this before
LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
@@ -257,6 +299,7 @@
}
// Signal only when writing the descriptors to ffs
android::base::SetProperty("sys.usb.ffs.ready", "1");
+ *out_control = std::move(control);
}
bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -271,7 +314,6 @@
return false;
}
- *out_control = std::move(control);
*out_bulk_in = std::move(bulk_in);
*out_bulk_out = std::move(bulk_out);
return true;
diff --git a/adb/daemon/usb_legacy.cpp b/adb/daemon/usb_legacy.cpp
index 7ace59d..fe80e7d 100644
--- a/adb/daemon/usb_legacy.cpp
+++ b/adb/daemon/usb_legacy.cpp
@@ -142,11 +142,12 @@
return orig_len;
}
-static int usb_ffs_read(usb_handle* h, void* data, int len) {
+static int usb_ffs_read(usb_handle* h, void* data, int len, bool allow_partial) {
D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
char* buf = static_cast<char*>(data);
int orig_len = len;
+ unsigned count = 0;
while (len > 0) {
int read_len = std::min(USB_FFS_BULK_SIZE, len);
int n = adb_read(h->bulk_out, buf, read_len);
@@ -156,6 +157,16 @@
}
buf += n;
len -= n;
+ count += n;
+
+ // For fastbootd command such as "getvar all", len parameter is always set 64.
+ // But what we read is actually less than 64.
+ // For example, length 10 for "getvar all" command.
+ // If we get less data than expected, this means there should be no more data.
+ if (allow_partial && n < read_len) {
+ orig_len = count;
+ break;
+ }
}
D("[ done fd=%d ]", h->bulk_out.get());
@@ -221,7 +232,7 @@
}
}
-static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
+static int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool allow_partial) {
return usb_ffs_do_aio(h, data, len, true);
}
@@ -299,7 +310,7 @@
}
int usb_read(usb_handle* h, void* data, int len) {
- return h->read(h, data, len);
+ return h->read(h, data, len, false /* allow_partial */);
}
int usb_close(usb_handle* h) {
@@ -307,6 +318,10 @@
return 0;
}
+void usb_reset(usb_handle* h) {
+ usb_close(h);
+}
+
void usb_kick(usb_handle* h) {
h->kick(h);
}
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 400b12f..95e1a28 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,24 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-
java_binary {
name: "deployagent",
sdk_version: "24",
srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
static_libs: ["apkzlib_zip"],
- wrapper: "deployagent/deployagent.sh",
proto: {
type: "lite",
+ },
+ dex_preopt: {
+ enabled: false,
}
-}
-
-java_binary_host {
- name: "deploypatchgenerator",
- srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
- static_libs: ["apkzlib"],
- manifest: "deploypatchgenerator/manifest.txt",
- proto: {
- type: "full",
- }
-}
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/OWNERS b/adb/fastdeploy/OWNERS
new file mode 100644
index 0000000..d145834
--- /dev/null
+++ b/adb/fastdeploy/OWNERS
@@ -0,0 +1 @@
+idries@google.com
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
index 4f17eb7..91576ca 100755
--- a/adb/fastdeploy/deployagent/deployagent.sh
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -1,7 +1,4 @@
-# Script to start "deployagent" on the device, which has a very rudimentary
-# shell.
-#
+#!/system/bin/sh
base=/data/local/tmp
export CLASSPATH=$base/deployagent.jar
exec app_process $base com.android.fastdeploy.DeployAgent "$@"
-
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index cd6f168..a8103c4 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -35,7 +35,7 @@
public final class DeployAgent {
private static final int BUFFER_SIZE = 128 * 1024;
- private static final int AGENT_VERSION = 0x00000001;
+ private static final int AGENT_VERSION = 0x00000002;
public static void main(String[] args) {
int exitCode = 0;
@@ -53,6 +53,15 @@
String packageName = args[1];
extractMetaData(packageName);
+ } else if (commandString.equals("find")) {
+ if (args.length != 2) {
+ showUsage(1);
+ }
+
+ String packageName = args[1];
+ if (getFilenameFromPackageName(packageName) == null) {
+ exitCode = 3;
+ }
} else if (commandString.equals("apply")) {
if (args.length < 4) {
showUsage(1);
@@ -112,6 +121,7 @@
"usage: deployagent <command> [<args>]\n\n" +
"commands:\n" +
"version get the version\n" +
+ "find PKGNAME return zero if package found, else non-zero\n" +
"extract PKGNAME extract an installed package's metadata\n" +
"apply PKGNAME PATCHFILE [-o|-pm] apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
" -o <FILE> directs output to FILE, default or - for stdout\n" +
@@ -134,7 +144,7 @@
return null;
}
- private static File getFileFromPackageName(String packageName) throws IOException {
+ private static String getFilenameFromPackageName(String packageName) throws IOException {
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.append("pm list packages -f " + packageName);
@@ -142,20 +152,36 @@
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String packagePrefix = "package:";
+ String packageSuffix = "=" + packageName;
String line = "";
while ((line = reader.readLine()) != null) {
- int packageIndex = line.indexOf(packagePrefix);
- int equalsIndex = line.indexOf("=" + packageName);
- return new File(line.substring(packageIndex + packagePrefix.length(), equalsIndex));
+ if (line.endsWith(packageSuffix)) {
+ int packageIndex = line.indexOf(packagePrefix);
+ if (packageIndex == -1) {
+ throw new IOException("error reading package list");
+ }
+ int equalsIndex = line.lastIndexOf(packageSuffix);
+ String fileName =
+ line.substring(packageIndex + packagePrefix.length(), equalsIndex);
+ return fileName;
+ }
}
-
return null;
}
+ private static File getFileFromPackageName(String packageName) throws IOException {
+ String filename = getFilenameFromPackageName(packageName);
+ if (filename == null) {
+ // Should not happen (function is only called when we know the package exists)
+ throw new IOException("package not found");
+ }
+ return new File(filename);
+ }
+
private static void extractMetaData(String packageName) throws IOException {
File apkFile = getFileFromPackageName(packageName);
APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
- apkMetaData.writeDelimitedTo(System.out);
+ apkMetaData.writeTo(System.out);
}
private static int createInstallSession(String[] args) throws IOException {
@@ -197,7 +223,6 @@
}
int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
-
if (writeExitCode == 0) {
return commitInstallSession(sessionId);
} else {
@@ -259,7 +284,6 @@
if (oldDataLen > 0) {
PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
}
-
newDataBytesWritten += copyLen + oldDataLen;
}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
index f0f00e1..c60f9a6 100644
--- a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -39,7 +39,7 @@
class PatchUtils {
private static final long NEGATIVE_MASK = 1L << 63;
private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
- public static final String SIGNATURE = "HAMADI/IHD";
+ public static final String SIGNATURE = "FASTDEPLOY";
private static long getOffsetFromEntry(StoredEntry entry) {
return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
new file mode 100644
index 0000000..22c9243
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "deploy_patch_generator.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "adb_unique_fd.h"
+#include "android-base/file.h"
+#include "patch_utils.h"
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+void DeployPatchGenerator::Log(const char* fmt, ...) {
+ if (!is_verbose_) {
+ return;
+ }
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+}
+
+void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
+ Log("Filename: %s", entry.filename().c_str());
+ Log("CRC32: 0x%08llX", entry.crc32());
+ Log("Data Offset: %lld", entry.dataoffset());
+ Log("Compressed Size: %lld", entry.compressedsize());
+ Log("Uncompressed Size: %lld", entry.uncompressedsize());
+}
+
+void DeployPatchGenerator::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
+ if (!is_verbose_) {
+ return;
+ }
+ Log("APK Metadata: %s", file);
+ for (int i = 0; i < metadata.entries_size(); i++) {
+ const APKEntry& entry = metadata.entries(i);
+ APKEntryToLog(entry);
+ }
+}
+
+void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
+ uint64_t totalSize) {
+ long totalEqualBytes = 0;
+ int totalEqualFiles = 0;
+ for (size_t i = 0; i < identicalEntries.size(); i++) {
+ if (identicalEntries[i].deviceEntry != nullptr) {
+ totalEqualBytes += identicalEntries[i].localEntry->compressedsize();
+ totalEqualFiles++;
+ }
+ }
+ float savingPercent = (totalEqualBytes * 100.0f) / totalSize;
+ fprintf(stderr, "Detected %d equal APK entries\n", totalEqualFiles);
+ fprintf(stderr, "%ld bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes, totalSize,
+ savingPercent);
+}
+
+void DeployPatchGenerator::GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+ const char* localApkPath, borrowed_fd output) {
+ unique_fd input(adb_open(localApkPath, O_RDONLY | O_CLOEXEC));
+ size_t newApkSize = adb_lseek(input, 0L, SEEK_END);
+ adb_lseek(input, 0L, SEEK_SET);
+
+ PatchUtils::WriteSignature(output);
+ PatchUtils::WriteLong(newApkSize, output);
+ size_t currentSizeOut = 0;
+ // Write data from the host upto the first entry we have that matches a device entry. Then write
+ // the metadata about the device entry and repeat for all entries that match on device. Finally
+ // write out any data left. If the device and host APKs are exactly the same this ends up
+ // writing out zip metadata from the local APK followed by offsets to the data to use from the
+ // device APK.
+ for (auto&& entry : entriesToUseOnDevice) {
+ int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
+ int64_t hostDataOffset = entry.localEntry->dataoffset();
+ int64_t deviceDataLength = entry.deviceEntry->compressedsize();
+ int64_t deltaFromDeviceDataStart = hostDataOffset - currentSizeOut;
+ PatchUtils::WriteLong(deltaFromDeviceDataStart, output);
+ if (deltaFromDeviceDataStart > 0) {
+ PatchUtils::Pipe(input, output, deltaFromDeviceDataStart);
+ }
+ PatchUtils::WriteLong(deviceDataOffset, output);
+ PatchUtils::WriteLong(deviceDataLength, output);
+ adb_lseek(input, deviceDataLength, SEEK_CUR);
+ currentSizeOut += deltaFromDeviceDataStart + deviceDataLength;
+ }
+ if (currentSizeOut != newApkSize) {
+ PatchUtils::WriteLong(newApkSize - currentSizeOut, output);
+ PatchUtils::Pipe(input, output, newApkSize - currentSizeOut);
+ PatchUtils::WriteLong(0, output);
+ PatchUtils::WriteLong(0, output);
+ }
+}
+
+bool DeployPatchGenerator::CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+ borrowed_fd output) {
+ std::string content;
+ APKMetaData deviceApkMetadata;
+ if (android::base::ReadFileToString(deviceApkMetadataPath, &content)) {
+ deviceApkMetadata.ParsePartialFromString(content);
+ } else {
+ // TODO: What do we want to do if we don't find any metadata.
+ // The current fallback behavior is to build a patch with the contents of |localApkPath|.
+ }
+
+ APKMetaData localApkMetadata = PatchUtils::GetAPKMetaData(localApkPath);
+ // Log gathered metadata info.
+ APKMetaDataToLog(deviceApkMetadataPath, deviceApkMetadata);
+ APKMetaDataToLog(localApkPath, localApkMetadata);
+
+ std::vector<SimpleEntry> identicalEntries;
+ uint64_t totalSize =
+ BuildIdenticalEntries(identicalEntries, localApkMetadata, deviceApkMetadata);
+ ReportSavings(identicalEntries, totalSize);
+ GeneratePatch(identicalEntries, localApkPath, output);
+ return true;
+}
+
+uint64_t DeployPatchGenerator::BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
+ const APKMetaData& localApkMetadata,
+ const APKMetaData& deviceApkMetadata) {
+ uint64_t totalSize = 0;
+ for (int i = 0; i < localApkMetadata.entries_size(); i++) {
+ const APKEntry& localEntry = localApkMetadata.entries(i);
+ totalSize += localEntry.compressedsize();
+ for (int j = 0; j < deviceApkMetadata.entries_size(); j++) {
+ const APKEntry& deviceEntry = deviceApkMetadata.entries(j);
+ if (deviceEntry.crc32() == localEntry.crc32() &&
+ deviceEntry.filename().compare(localEntry.filename()) == 0) {
+ SimpleEntry simpleEntry;
+ simpleEntry.localEntry = const_cast<APKEntry*>(&localEntry);
+ simpleEntry.deviceEntry = const_cast<APKEntry*>(&deviceEntry);
+ APKEntryToLog(localEntry);
+ outIdenticalEntries.push_back(simpleEntry);
+ break;
+ }
+ }
+ }
+ std::sort(outIdenticalEntries.begin(), outIdenticalEntries.end(),
+ [](const SimpleEntry& lhs, const SimpleEntry& rhs) {
+ return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
+ });
+ return totalSize;
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
new file mode 100644
index 0000000..30e41a5
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * This class is responsible for creating a patch that can be accepted by the deployagent. The
+ * patch format is documented in GeneratePatch.
+ */
+class DeployPatchGenerator {
+ public:
+ /**
+ * Simple struct to hold mapping between local metadata and device metadata.
+ */
+ struct SimpleEntry {
+ com::android::fastdeploy::APKEntry* localEntry;
+ com::android::fastdeploy::APKEntry* deviceEntry;
+ };
+
+ /**
+ * If |is_verbose| is true ApkEntries that are similar between device and host are written to
+ * the console.
+ */
+ explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
+ /**
+ * Given a |localApkPath|, and the |deviceApkMetadataPath| from an installed APK this function
+ * writes a patch to the given |output|.
+ */
+ bool CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+ android::base::borrowed_fd output);
+
+ private:
+ bool is_verbose_;
+
+ /**
+ * Log function only logs data to stdout when |is_verbose_| is true.
+ */
+ void Log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+
+ /**
+ * Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
+ * early outs. |file| is the path to the file represented by |metadata|. This function is used
+ * for debugging / information.
+ */
+ void APKMetaDataToLog(const char* file, const com::android::fastdeploy::APKMetaData& metadata);
+ /**
+ * Helper function to log APKEntry.
+ */
+ void APKEntryToLog(const com::android::fastdeploy::APKEntry& entry);
+
+ /**
+ * Helper function to report savings by fastdeploy. This function prints out savings even with
+ * |is_verbose_| set to false. |totalSize| is used to show a percentage of savings. Note:
+ * |totalSize| is the size of the ZipEntries. Not the size of the entire file. The metadata of
+ * the zip data needs to be sent across with every iteration.
+ * [Patch format]
+ * |Fixed String| Signature
+ * |long| New Size of Apk
+ * |Packets[]| Array of Packets
+ *
+ * [Packet Format]
+ * |long| Size of data to use from patch
+ * |byte[]| Patch data
+ * |long| Offset of data to use already on device
+ * |long| Length of data to read from device APK
+ * TODO(b/138306784): Move the patch format to a proto.
+ */
+ void ReportSavings(const std::vector<SimpleEntry>& identicalEntries, uint64_t totalSize);
+
+ /**
+ * This enumerates each entry in |entriesToUseOnDevice| and builds a patch file copying data
+ * from |localApkPath| where we are unable to use entries already on the device. The new patch
+ * is written to |output|. The entries are expected to be sorted by data offset from lowest to
+ * highest.
+ */
+ void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+ const char* localApkPath, android::base::borrowed_fd output);
+
+ protected:
+ uint64_t BuildIdenticalEntries(
+ std::vector<SimpleEntry>& outIdenticalEntries,
+ const com::android::fastdeploy::APKMetaData& localApkMetadata,
+ const com::android::fastdeploy::APKMetaData& deviceApkMetadataPath);
+};
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
new file mode 100644
index 0000000..9cdc44e
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "deploy_patch_generator.h"
+#include "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+ return "fastdeploy/testdata/" + name;
+}
+
+class TestPatchGenerator : DeployPatchGenerator {
+ public:
+ TestPatchGenerator() : DeployPatchGenerator(false) {}
+ void GatherIdenticalEntries(std::vector<DeployPatchGenerator::SimpleEntry>& outIdenticalEntries,
+ const APKMetaData& metadataA, const APKMetaData& metadataB) {
+ BuildIdenticalEntries(outIdenticalEntries, metadataA, metadataB);
+ }
+};
+
+TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
+ std::string apkPath = GetTestFile("rotating_cube-release.apk");
+ APKMetaData metadataA = PatchUtils::GetAPKMetaData(apkPath.c_str());
+ TestPatchGenerator generator;
+ std::vector<DeployPatchGenerator::SimpleEntry> entries;
+ generator.GatherIdenticalEntries(entries, metadataA, metadataA);
+ // Expect the entry count to match the number of entries in the metadata.
+ const uint32_t identicalCount = entries.size();
+ const uint32_t entriesCount = metadataA.entries_size();
+ EXPECT_EQ(identicalCount, entriesCount);
+}
+
+TEST(DeployPatchGeneratorTest, NoDeviceMetadata) {
+ std::string apkPath = GetTestFile("rotating_cube-release.apk");
+ // Get size of our test apk.
+ long apkSize = 0;
+ {
+ unique_fd apkFile(adb_open(apkPath.c_str(), O_RDWR));
+ apkSize = adb_lseek(apkFile, 0L, SEEK_END);
+ }
+
+ // Create a patch that is 100% different.
+ TemporaryFile output;
+ DeployPatchGenerator generator(true);
+ generator.CreatePatch(apkPath.c_str(), "", output.fd);
+
+ // Expect a patch file that has a size at least the size of our initial APK.
+ long patchSize = adb_lseek(output.fd, 0L, SEEK_END);
+ EXPECT_GT(patchSize, apkSize);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
deleted file mode 100644
index 5c00505..0000000
--- a/adb/fastdeploy/deploypatchgenerator/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
new file mode 100644
index 0000000..f11ddd1
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "patch_utils.h"
+
+#include <androidfw/ZipFileRO.h>
+#include <stdio.h>
+
+#include "adb_io.h"
+#include "android-base/endian.h"
+#include "sysdeps.h"
+
+using namespace com::android;
+using namespace com::android::fastdeploy;
+using namespace android::base;
+
+static constexpr char kSignature[] = "FASTDEPLOY";
+
+APKMetaData PatchUtils::GetAPKMetaData(const char* apkPath) {
+ APKMetaData apkMetaData;
+#undef open
+ std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
+ if (zipFile == nullptr) {
+ printf("Could not open %s", apkPath);
+ exit(1);
+ }
+ void* cookie;
+ if (zipFile->startIteration(&cookie)) {
+ android::ZipEntryRO entry;
+ while ((entry = zipFile->nextEntry(cookie)) != NULL) {
+ char fileName[256];
+ // Make sure we have a file name.
+ // TODO: Handle filenames longer than 256.
+ if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
+ continue;
+ }
+
+ uint32_t uncompressedSize, compressedSize, crc32;
+ int64_t dataOffset;
+ zipFile->getEntryInfo(entry, nullptr, &uncompressedSize, &compressedSize, &dataOffset,
+ nullptr, &crc32);
+ APKEntry* apkEntry = apkMetaData.add_entries();
+ apkEntry->set_crc32(crc32);
+ apkEntry->set_filename(fileName);
+ apkEntry->set_compressedsize(compressedSize);
+ apkEntry->set_uncompressedsize(uncompressedSize);
+ apkEntry->set_dataoffset(dataOffset);
+ }
+ }
+ return apkMetaData;
+}
+
+void PatchUtils::WriteSignature(borrowed_fd output) {
+ WriteFdExactly(output, kSignature, sizeof(kSignature) - 1);
+}
+
+void PatchUtils::WriteLong(int64_t value, borrowed_fd output) {
+ int64_t toLittleEndian = htole64(value);
+ WriteFdExactly(output, &toLittleEndian, sizeof(int64_t));
+}
+
+void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) {
+ constexpr static int BUFFER_SIZE = 128 * 1024;
+ char buffer[BUFFER_SIZE];
+ size_t transferAmount = 0;
+ while (transferAmount != amount) {
+ long chunkAmount =
+ amount - transferAmount > BUFFER_SIZE ? BUFFER_SIZE : amount - transferAmount;
+ long readAmount = adb_read(input, buffer, chunkAmount);
+ WriteFdExactly(output, buffer, readAmount);
+ transferAmount += readAmount;
+ }
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.h b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
new file mode 100644
index 0000000..0ebfe8f
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * Helper class that mirrors the PatchUtils from deploy agent.
+ */
+class PatchUtils {
+ public:
+ /**
+ * This function takes a local APK file and builds the APKMetaData required by the patching
+ * algorithm. The if this function has an error a string is printed to the terminal and exit(1)
+ * is called.
+ */
+ static com::android::fastdeploy::APKMetaData GetAPKMetaData(const char* file);
+ /**
+ * Writes a fixed signature string to the header of the patch.
+ */
+ static void WriteSignature(android::base::borrowed_fd output);
+ /**
+ * Writes an int64 to the |output| reversing the bytes.
+ */
+ static void WriteLong(int64_t value, android::base::borrowed_fd output);
+ /**
+ * Copy |amount| of data from |input| to |output|.
+ */
+ static void Pipe(android::base::borrowed_fd input, android::base::borrowed_fd output,
+ size_t amount);
+};
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
new file mode 100644
index 0000000..a7eeebf
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+#include <string>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+ return "fastdeploy/testdata/" + name;
+}
+
+bool FileMatchesContent(android::base::borrowed_fd input, const char* contents,
+ ssize_t contentsSize) {
+ adb_lseek(input, 0, SEEK_SET);
+ // Use a temp buffer larger than any test contents.
+ constexpr int BUFFER_SIZE = 2048;
+ char buffer[BUFFER_SIZE];
+ bool result = true;
+ // Validate size of files is equal.
+ ssize_t readAmount = adb_read(input, buffer, BUFFER_SIZE);
+ EXPECT_EQ(readAmount, contentsSize);
+ result = memcmp(buffer, contents, readAmount) == 0;
+ for (int i = 0; i < readAmount; i++) {
+ printf("%x", buffer[i]);
+ }
+ printf(" == ");
+ for (int i = 0; i < contentsSize; i++) {
+ printf("%x", contents[i]);
+ }
+ printf("\n");
+
+ return result;
+}
+
+TEST(PatchUtilsTest, SwapLongWrites) {
+ TemporaryFile output;
+ PatchUtils::WriteLong(0x0011223344556677, output.fd);
+ adb_lseek(output.fd, 0, SEEK_SET);
+ const char expected[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+ EXPECT_TRUE(FileMatchesContent(output.fd, expected, 8));
+}
+
+TEST(PatchUtilsTest, PipeWritesAmountToOutput) {
+ std::string expected("Some Data");
+ TemporaryFile input;
+ TemporaryFile output;
+ // Populate input file.
+ WriteFdExactly(input.fd, expected);
+ adb_lseek(input.fd, 0, SEEK_SET);
+ // Open input file for read, and output file for write.
+ PatchUtils::Pipe(input.fd, output.fd, expected.size());
+ // Validate pipe worked
+ EXPECT_TRUE(FileMatchesContent(output.fd, expected.c_str(), expected.size()));
+}
+
+TEST(PatchUtilsTest, SignatureConstMatches) {
+ std::string apkFile = GetTestFile("rotating_cube-release.apk");
+ TemporaryFile output;
+ PatchUtils::WriteSignature(output.fd);
+ std::string contents("FASTDEPLOY");
+ EXPECT_TRUE(FileMatchesContent(output.fd, contents.c_str(), contents.size()));
+}
+
+TEST(PatchUtilsTest, GatherMetadata) {
+ std::string apkFile = GetTestFile("rotating_cube-release.apk");
+ APKMetaData metadata = PatchUtils::GetAPKMetaData(apkFile.c_str());
+ std::string expectedMetadata;
+ android::base::ReadFileToString(GetTestFile("rotating_cube-metadata-release.data"),
+ &expectedMetadata);
+ std::string actualMetadata;
+ metadata.SerializeToString(&actualMetadata);
+ EXPECT_EQ(expectedMetadata, actualMetadata);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
deleted file mode 100644
index 5577364..0000000
--- a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fastdeploy;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.StringBuilder;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.ArrayList;
-
-import java.nio.charset.StandardCharsets;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.AbstractMap.SimpleEntry;
-
-import com.android.fastdeploy.APKMetaData;
-import com.android.fastdeploy.APKEntry;
-
-public final class DeployPatchGenerator {
- private static final int BUFFER_SIZE = 128 * 1024;
-
- public static void main(String[] args) {
- try {
- if (args.length < 2) {
- showUsage(0);
- }
-
- boolean verbose = false;
- if (args.length > 2) {
- String verboseFlag = args[2];
- if (verboseFlag.compareTo("--verbose") == 0) {
- verbose = true;
- }
- }
-
- StringBuilder sb = null;
- String apkPath = args[0];
- String deviceMetadataPath = args[1];
- File hostFile = new File(apkPath);
-
- List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
- if (verbose) {
- sb = new StringBuilder();
- for (APKEntry entry : deviceZipEntries) {
- APKEntryToString(entry, sb);
- }
- System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
- System.err.println(sb.toString());
- }
-
- List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
- if (verbose) {
- sb = new StringBuilder();
- for (APKEntry entry : hostFileEntries) {
- APKEntryToString(entry, sb);
- }
- System.err.println("Host Entries (" + hostFileEntries.size() + ")");
- System.err.println(sb.toString());
- }
-
- List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet =
- getIdenticalContents(deviceZipEntries, hostFileEntries);
- reportIdenticalContents(identicalContentsEntrySet, hostFile);
-
- if (verbose) {
- sb = new StringBuilder();
- for (SimpleEntry<APKEntry, APKEntry> identicalEntry : identicalContentsEntrySet) {
- APKEntry entry = identicalEntry.getValue();
- APKEntryToString(entry, sb);
- }
- System.err.println("Identical Entries (" + identicalContentsEntrySet.size() + ")");
- System.err.println(sb.toString());
- }
-
- createPatch(identicalContentsEntrySet, hostFile, System.out);
- } catch (Exception e) {
- System.err.println("Error: " + e);
- e.printStackTrace();
- System.exit(2);
- }
- System.exit(0);
- }
-
- private static void showUsage(int exitCode) {
- System.err.println("usage: deploypatchgenerator <apkpath> <deviceapkmetadata> [--verbose]");
- System.err.println("");
- System.exit(exitCode);
- }
-
- private static void APKEntryToString(APKEntry entry, StringBuilder outputString) {
- outputString.append(String.format("Filename: %s\n", entry.getFileName()));
- outputString.append(String.format("CRC32: 0x%08X\n", entry.getCrc32()));
- outputString.append(String.format("Data Offset: %d\n", entry.getDataOffset()));
- outputString.append(String.format("Compressed Size: %d\n", entry.getCompressedSize()));
- outputString.append(String.format("Uncompressed Size: %d\n", entry.getUncompressedSize()));
- }
-
- private static List<APKEntry> getMetadataFromFile(String deviceMetadataPath) throws IOException {
- InputStream is = new FileInputStream(new File(deviceMetadataPath));
- APKMetaData apkMetaData = APKMetaData.parseDelimitedFrom(is);
- return apkMetaData.getEntriesList();
- }
-
- private static List<SimpleEntry<APKEntry, APKEntry>> getIdenticalContents(
- List<APKEntry> deviceZipEntries, List<APKEntry> hostZipEntries) throws IOException {
- List<SimpleEntry<APKEntry, APKEntry>> identicalContents =
- new ArrayList<SimpleEntry<APKEntry, APKEntry>>();
-
- for (APKEntry deviceZipEntry : deviceZipEntries) {
- for (APKEntry hostZipEntry : hostZipEntries) {
- if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32()) {
- identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
- }
- }
- }
-
- Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
- @Override
- public int compare(
- SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
- return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
- }
- });
-
- return identicalContents;
- }
-
- private static void reportIdenticalContents(
- List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
- throws IOException {
- long totalEqualBytes = 0;
- int totalEqualFiles = 0;
- for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
- APKEntry hostAPKEntry = entries.getValue();
- totalEqualBytes += hostAPKEntry.getCompressedSize();
- totalEqualFiles++;
- }
-
- float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
-
- System.err.println("Detected " + totalEqualFiles + " equal APK entries");
- System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
- + savingPercent + "%)");
- }
-
- static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
- File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
- FileInputStream hostFileInputStream = new FileInputStream(hostFile);
-
- patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
- PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
-
- byte[] buffer = new byte[BUFFER_SIZE];
- long totalBytesWritten = 0;
- Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
- zipEntrySimpleEntrys.iterator();
- while (entrySimpleEntryIterator.hasNext()) {
- SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
- APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
- APKEntry hostAPKEntry = entrySimpleEntry.getValue();
-
- long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
- long oldDataOffset = deviceAPKEntry.getDataOffset();
- long oldDataLen = deviceAPKEntry.getCompressedSize();
-
- PatchUtils.writeFormattedLong(newDataLen, patchStream);
- PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
- PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
- PatchUtils.writeFormattedLong(oldDataLen, patchStream);
-
- long skip = hostFileInputStream.skip(oldDataLen);
- if (skip != oldDataLen) {
- throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
- + " bytes but return code was " + skip);
- }
- totalBytesWritten += oldDataLen + newDataLen;
- }
- long remainderLen = hostFile.length() - totalBytesWritten;
- PatchUtils.writeFormattedLong(remainderLen, patchStream);
- PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
- PatchUtils.writeFormattedLong(0, patchStream);
- PatchUtils.writeFormattedLong(0, patchStream);
- patchStream.flush();
- }
-}
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
new file mode 100644
index 0000000..0671bf3
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
@@ -0,0 +1,6 @@
+
+#ÇÏ«META-INF/MANIFEST.MFÇ Q(W
+#AndroidManifest.xml1 ä(è
+6¦µ>#lib/armeabi-v7a/libvulkan_sample.so ÀÒQ(²ì
+ ãresources.arscôàQ ´(´
+ÂÉclasses.dexÁ ÿ(ô
diff --git a/adb/fastdeploy/testdata/rotating_cube-release.apk b/adb/fastdeploy/testdata/rotating_cube-release.apk
new file mode 100644
index 0000000..d47e0ea
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-release.apk
Binary files differ
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
deleted file mode 100644
index e096560..0000000
--- a/adb/fdevent.cpp
+++ /dev/null
@@ -1,476 +0,0 @@
-/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
-**
-** Copyright 2006, Brian Swetland <swetland@frotz.net>
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define TRACE_TAG FDEVENT
-
-#include "sysdeps.h"
-#include "fdevent.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <deque>
-#include <functional>
-#include <list>
-#include <mutex>
-#include <unordered_map>
-#include <vector>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/thread_annotations.h>
-#include <android-base/threads.h>
-
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "sysdeps/chrono.h"
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-struct PollNode {
- fdevent* fde;
- adb_pollfd pollfd;
-
- explicit PollNode(fdevent* fde) : fde(fde) {
- memset(&pollfd, 0, sizeof(pollfd));
- pollfd.fd = fde->fd.get();
-
-#if defined(__linux__)
- // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
- // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
- pollfd.events = POLLRDHUP;
-#endif
- }
-};
-
-// All operations to fdevent should happen only in the main thread.
-// That's why we don't need a lock for fdevent.
-static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
-static auto& g_pending_list = *new std::list<fdevent*>();
-static std::atomic<bool> terminate_loop(false);
-static bool main_thread_valid;
-static uint64_t main_thread_id;
-
-static uint64_t fdevent_id;
-
-static bool run_needs_flush = false;
-static auto& run_queue_notify_fd = *new unique_fd();
-static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
-
-void check_main_thread() {
- if (main_thread_valid) {
- CHECK_EQ(main_thread_id, android::base::GetThreadId());
- }
-}
-
-void set_main_thread() {
- main_thread_valid = true;
- main_thread_id = android::base::GetThreadId();
-}
-
-static std::string dump_fde(const fdevent* fde) {
- std::string state;
- if (fde->state & FDE_ACTIVE) {
- state += "A";
- }
- if (fde->state & FDE_PENDING) {
- state += "P";
- }
- if (fde->state & FDE_CREATED) {
- state += "C";
- }
- if (fde->state & FDE_READ) {
- state += "R";
- }
- if (fde->state & FDE_WRITE) {
- state += "W";
- }
- if (fde->state & FDE_ERROR) {
- state += "E";
- }
- return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
- state.c_str());
-}
-
-void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
- check_main_thread();
- CHECK_GE(fd, 0);
- memset(fde, 0, sizeof(fdevent));
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
- check_main_thread();
- CHECK_GE(fd, 0);
-
- fdevent* fde = new fdevent();
- fde->id = fdevent_id++;
- fde->state = FDE_ACTIVE;
- fde->fd.reset(fd);
- fde->func = func;
- fde->arg = arg;
- if (!set_file_block_mode(fd, false)) {
- // Here is not proper to handle the error. If it fails here, some error is
- // likely to be detected by poll(), then we can let the callback function
- // to handle it.
- LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
- }
- auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
- CHECK(pair.second) << "install existing fd " << fd;
-
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-unique_fd fdevent_release(fdevent* fde) {
- check_main_thread();
- if (!fde) {
- return {};
- }
-
- if (!(fde->state & FDE_CREATED)) {
- LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
- }
-
- unique_fd result = std::move(fde->fd);
- if (fde->state & FDE_ACTIVE) {
- g_poll_node_map.erase(result.get());
-
- if (fde->state & FDE_PENDING) {
- g_pending_list.remove(fde);
- }
- fde->state = 0;
- fde->events = 0;
- }
-
- delete fde;
- return result;
-}
-
-void fdevent_destroy(fdevent* fde) {
- // Release, and then let unique_fd's destructor cleanup.
- fdevent_release(fde);
-}
-
-static void fdevent_update(fdevent* fde, unsigned events) {
- auto it = g_poll_node_map.find(fde->fd.get());
- CHECK(it != g_poll_node_map.end());
- PollNode& node = it->second;
- if (events & FDE_READ) {
- node.pollfd.events |= POLLIN;
- } else {
- node.pollfd.events &= ~POLLIN;
- }
-
- if (events & FDE_WRITE) {
- node.pollfd.events |= POLLOUT;
- } else {
- node.pollfd.events &= ~POLLOUT;
- }
- fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-void fdevent_set(fdevent* fde, unsigned events) {
- check_main_thread();
- events &= FDE_EVENTMASK;
- if ((fde->state & FDE_EVENTMASK) == events) {
- return;
- }
- CHECK(fde->state & FDE_ACTIVE);
- fdevent_update(fde, events);
- D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
-
- if (fde->state & FDE_PENDING) {
- // If we are pending, make sure we don't signal an event that is no longer wanted.
- fde->events &= events;
- if (fde->events == 0) {
- g_pending_list.remove(fde);
- fde->state &= ~FDE_PENDING;
- }
- }
-}
-
-void fdevent_add(fdevent* fde, unsigned events) {
- check_main_thread();
- fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
-}
-
-void fdevent_del(fdevent* fde, unsigned events) {
- check_main_thread();
- fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
-}
-
-static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
- std::string result;
- for (const auto& pollfd : pollfds) {
- std::string op;
- if (pollfd.events & POLLIN) {
- op += "R";
- }
- if (pollfd.events & POLLOUT) {
- op += "W";
- }
- android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
- }
- return result;
-}
-
-static void fdevent_process() {
- std::vector<adb_pollfd> pollfds;
- for (const auto& pair : g_poll_node_map) {
- pollfds.push_back(pair.second.pollfd);
- }
- CHECK_GT(pollfds.size(), 0u);
- D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-
- int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
- if (ret == -1) {
- PLOG(ERROR) << "poll(), ret = " << ret;
- return;
- }
- for (const auto& pollfd : pollfds) {
- if (pollfd.revents != 0) {
- D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
- }
- unsigned events = 0;
- if (pollfd.revents & POLLIN) {
- events |= FDE_READ;
- }
- if (pollfd.revents & POLLOUT) {
- events |= FDE_WRITE;
- }
- if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
- // We fake a read, as the rest of the code assumes that errors will
- // be detected at that point.
- events |= FDE_READ | FDE_ERROR;
- }
-#if defined(__linux__)
- if (pollfd.revents & POLLRDHUP) {
- events |= FDE_READ | FDE_ERROR;
- }
-#endif
- if (events != 0) {
- auto it = g_poll_node_map.find(pollfd.fd);
- CHECK(it != g_poll_node_map.end());
- fdevent* fde = it->second.fde;
- CHECK_EQ(fde->fd.get(), pollfd.fd);
- fde->events |= events;
- D("%s got events %x", dump_fde(fde).c_str(), events);
- fde->state |= FDE_PENDING;
- g_pending_list.push_back(fde);
- }
- }
-}
-
-static void fdevent_call_fdfunc(fdevent* fde) {
- unsigned events = fde->events;
- fde->events = 0;
- CHECK(fde->state & FDE_PENDING);
- fde->state &= (~FDE_PENDING);
- D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
- fde->func(fde->fd.get(), events, fde->arg);
-}
-
-static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
- // We need to be careful around reentrancy here, since a function we call can queue up another
- // function.
- while (true) {
- std::function<void()> fn;
- {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- if (run_queue.empty()) {
- break;
- }
- fn = run_queue.front();
- run_queue.pop_front();
- }
- fn();
- }
-}
-
-static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
- CHECK_GE(fd, 0);
- CHECK(ev & FDE_READ);
-
- char buf[1024];
-
- // Empty the fd.
- if (adb_read(fd, buf, sizeof(buf)) == -1) {
- PLOG(FATAL) << "failed to empty run queue notify fd";
- }
-
- // Mark that we need to flush, and then run it at the end of fdevent_loop.
- run_needs_flush = true;
-}
-
-static void fdevent_run_setup() {
- {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- CHECK(run_queue_notify_fd.get() == -1);
- int s[2];
- if (adb_socketpair(s) != 0) {
- PLOG(FATAL) << "failed to create run queue notify socketpair";
- }
-
- if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
- PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
- }
-
- run_queue_notify_fd.reset(s[0]);
- fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
- CHECK(fde != nullptr);
- fdevent_add(fde, FDE_READ);
- }
-
- fdevent_run_flush();
-}
-
-void fdevent_run_on_main_thread(std::function<void()> fn) {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- run_queue.push_back(std::move(fn));
-
- // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
- // In that case, rely on the setup code to flush the queue without a notification being needed.
- if (run_queue_notify_fd != -1) {
- int rc = adb_write(run_queue_notify_fd.get(), "", 1);
-
- // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
- if (rc == 0) {
- PLOG(FATAL) << "run queue notify fd was closed?";
- } else if (rc == -1 && errno != EAGAIN) {
- PLOG(FATAL) << "failed to write to run queue notify fd";
- }
- }
-}
-
-static void fdevent_check_spin(uint64_t cycle) {
- // Check to see if we're spinning because we forgot about an fdevent
- // by keeping track of how long fdevents have been continuously pending.
- struct SpinCheck {
- fdevent* fde;
- android::base::boot_clock::time_point timestamp;
- uint64_t cycle;
- };
- static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
- static auto last_cycle = android::base::boot_clock::now();
-
- auto now = android::base::boot_clock::now();
- if (now - last_cycle > 10ms) {
- // We're not spinning.
- g_continuously_pending.clear();
- last_cycle = now;
- return;
- }
- last_cycle = now;
-
- for (auto* fde : g_pending_list) {
- auto it = g_continuously_pending.find(fde->id);
- if (it == g_continuously_pending.end()) {
- g_continuously_pending[fde->id] =
- SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
- } else {
- it->second.cycle = cycle;
- }
- }
-
- for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
- if (it->second.cycle != cycle) {
- it = g_continuously_pending.erase(it);
- } else {
- // Use an absurdly long window, since all we really care about is
- // getting a bugreport eventually.
- if (now - it->second.timestamp > 300s) {
- LOG(FATAL_WITHOUT_ABORT)
- << "detected spin in fdevent: " << dump_fde(it->second.fde);
-#if defined(__linux__)
- int fd = it->second.fde->fd.get();
- std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
- std::string path;
- if (!android::base::Readlink(fd_path, &path)) {
- PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
- }
- LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
-#endif
- abort();
- }
- ++it;
- }
- }
-}
-
-void fdevent_loop() {
- set_main_thread();
- fdevent_run_setup();
-
- uint64_t cycle = 0;
- while (true) {
- if (terminate_loop) {
- return;
- }
-
- D("--- --- waiting for events");
-
- fdevent_process();
-
- fdevent_check_spin(cycle++);
-
- while (!g_pending_list.empty()) {
- fdevent* fde = g_pending_list.front();
- g_pending_list.pop_front();
- fdevent_call_fdfunc(fde);
- }
-
- if (run_needs_flush) {
- fdevent_run_flush();
- run_needs_flush = false;
- }
- }
-}
-
-void fdevent_terminate_loop() {
- terminate_loop = true;
-}
-
-size_t fdevent_installed_count() {
- return g_poll_node_map.size();
-}
-
-void fdevent_reset() {
- g_poll_node_map.clear();
- g_pending_list.clear();
-
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- run_queue_notify_fd.reset();
- run_queue.clear();
-
- main_thread_valid = false;
- terminate_loop = false;
-}
diff --git a/adb/fdevent.h b/adb/fdevent.h
deleted file mode 100644
index df2339a..0000000
--- a/adb/fdevent.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FDEVENT_H
-#define __FDEVENT_H
-
-#include <stddef.h>
-#include <stdint.h> /* for int64_t */
-
-#include <functional>
-
-#include "adb_unique_fd.h"
-
-/* events that may be observed */
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-struct fdevent {
- uint64_t id;
-
- unique_fd fd;
- int force_eof = 0;
-
- uint16_t state = 0;
- uint16_t events = 0;
-
- fd_func func = nullptr;
- void* arg = nullptr;
-};
-
-/* Allocate and initialize a new fdevent object
- * Note: use FD_TIMER as 'fd' to create a fd-less object
- * (used to implement timers).
-*/
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-
-// Deallocate an fdevent object that was created by fdevent_create.
-void fdevent_destroy(fdevent *fde);
-
-// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
-unique_fd fdevent_release(fdevent* fde);
-
-/* Change which events should cause notifications
-*/
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-
-void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
-
-/* loop forever, handling events.
-*/
-void fdevent_loop();
-
-void check_main_thread();
-
-// Queue an operation to run on the main thread.
-void fdevent_run_on_main_thread(std::function<void()> fn);
-
-// The following functions are used only for tests.
-void fdevent_terminate_loop();
-size_t fdevent_installed_count();
-void fdevent_reset();
-void set_main_thread();
-
-#endif
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
new file mode 100644
index 0000000..28b8f37
--- /dev/null
+++ b/adb/fdevent/fdevent.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2006, Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "fdevent_poll.h"
+
+std::string dump_fde(const fdevent* fde) {
+ std::string state;
+ if (fde->state & FDE_ACTIVE) {
+ state += "A";
+ }
+ if (fde->state & FDE_PENDING) {
+ state += "P";
+ }
+ if (fde->state & FDE_READ) {
+ state += "R";
+ }
+ if (fde->state & FDE_WRITE) {
+ state += "W";
+ }
+ if (fde->state & FDE_ERROR) {
+ state += "E";
+ }
+ return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+ state.c_str());
+}
+
+fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) {
+ CheckMainThread();
+ CHECK_GE(fd.get(), 0);
+
+ fdevent* fde = new fdevent();
+ fde->id = fdevent_id_++;
+ fde->state = FDE_ACTIVE;
+ fde->fd = std::move(fd);
+ fde->func = func;
+ fde->arg = arg;
+ if (!set_file_block_mode(fde->fd, false)) {
+ // Here is not proper to handle the error. If it fails here, some error is
+ // likely to be detected by poll(), then we can let the callback function
+ // to handle it.
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
+ }
+
+ this->Register(fde);
+ return fde;
+}
+
+unique_fd fdevent_context::Destroy(fdevent* fde) {
+ CheckMainThread();
+ if (!fde) {
+ return {};
+ }
+
+ this->Unregister(fde);
+
+ unique_fd result = std::move(fde->fd);
+ delete fde;
+ return result;
+}
+
+void fdevent_context::Add(fdevent* fde, unsigned events) {
+ Set(fde, (fde->state & FDE_EVENTMASK) | events);
+}
+
+void fdevent_context::Del(fdevent* fde, unsigned events) {
+ CHECK(!(events & FDE_TIMEOUT));
+ Set(fde, (fde->state & FDE_EVENTMASK) & ~events);
+}
+
+void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+ CheckMainThread();
+ fde->timeout = timeout;
+ fde->last_active = std::chrono::steady_clock::now();
+}
+
+void fdevent_context::CheckMainThread() {
+ if (main_thread_id_) {
+ CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
+ }
+}
+
+void fdevent_context::Run(std::function<void()> fn) {
+ {
+ std::lock_guard<std::mutex> lock(run_queue_mutex_);
+ run_queue_.push_back(std::move(fn));
+ }
+
+ Interrupt();
+}
+
+void fdevent_context::TerminateLoop() {
+ terminate_loop_ = true;
+ Interrupt();
+}
+
+void fdevent_context::FlushRunQueue() {
+ // We need to be careful around reentrancy here, since a function we call can queue up another
+ // function.
+ while (true) {
+ std::function<void()> fn;
+ {
+ std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
+ if (this->run_queue_.empty()) {
+ break;
+ }
+ fn = this->run_queue_.front();
+ this->run_queue_.pop_front();
+ }
+ fn();
+ }
+}
+
+static auto& g_ambient_fdevent_context =
+ *new std::unique_ptr<fdevent_context>(new fdevent_context_poll());
+
+static fdevent_context* fdevent_get_ambient() {
+ return g_ambient_fdevent_context.get();
+}
+
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+ unique_fd ufd(fd);
+ return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
+ unique_fd ufd(fd);
+ return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+unique_fd fdevent_release(fdevent* fde) {
+ return fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_destroy(fdevent* fde) {
+ fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_set(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Set(fde, events);
+}
+
+void fdevent_add(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Add(fde, events);
+}
+
+void fdevent_del(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Del(fde, events);
+}
+
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+ fdevent_get_ambient()->SetTimeout(fde, timeout);
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+ fdevent_get_ambient()->Run(std::move(fn));
+}
+
+void fdevent_loop() {
+ fdevent_get_ambient()->Loop();
+}
+
+void check_main_thread() {
+ fdevent_get_ambient()->CheckMainThread();
+}
+
+void fdevent_terminate_loop() {
+ fdevent_get_ambient()->TerminateLoop();
+}
+
+size_t fdevent_installed_count() {
+ return fdevent_get_ambient()->InstalledCount();
+}
+
+void fdevent_reset() {
+ g_ambient_fdevent_context.reset(new fdevent_context_poll());
+}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
new file mode 100644
index 0000000..ccb0c92
--- /dev/null
+++ b/adb/fdevent/fdevent.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <variant>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+
+// Events that may be observed
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
+
+// Internal states.
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
+
+struct fdevent;
+std::string dump_fde(const fdevent* fde);
+
+struct fdevent_context {
+ public:
+ virtual ~fdevent_context() = default;
+
+ // Allocate and initialize a new fdevent object.
+ fdevent* Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg);
+
+ // Deallocate an fdevent object, returning the file descriptor that was owned by it.
+ unique_fd Destroy(fdevent* fde);
+
+ protected:
+ // Register an fdevent that is being created by Create with the fdevent_context.
+ virtual void Register(fdevent* fde) = 0;
+
+ // Unregister an fdevent that is being destroyed by Destroy with the fdevent_context.
+ virtual void Unregister(fdevent* fde) = 0;
+
+ public:
+ // Change which events should cause notifications.
+ virtual void Set(fdevent* fde, unsigned events) = 0;
+ void Add(fdevent* fde, unsigned events);
+ void Del(fdevent* fde, unsigned events);
+
+ // Set a timeout on an fdevent.
+ // If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
+ // Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
+ // trigger repeatedly every |timeout| ms.
+ void SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+
+ // Loop until TerminateLoop is called, handling events.
+ // Implementations should call FlushRunQueue on every iteration, and check the value of
+ // terminate_loop_ to determine whether to stop.
+ virtual void Loop() = 0;
+
+ // Assert that the caller is either running on the context's main thread, or that there is no
+ // active main thread.
+ void CheckMainThread();
+
+ // Queue an operation to be run on the main thread.
+ void Run(std::function<void()> fn);
+
+ // Test-only functionality:
+ void TerminateLoop();
+ virtual size_t InstalledCount() = 0;
+
+ protected:
+ // Interrupt the run loop.
+ virtual void Interrupt() = 0;
+
+ // Run all pending functions enqueued via Run().
+ void FlushRunQueue() EXCLUDES(run_queue_mutex_);
+
+ std::optional<uint64_t> main_thread_id_ = std::nullopt;
+ std::atomic<bool> terminate_loop_ = false;
+
+ private:
+ uint64_t fdevent_id_ = 0;
+ std::mutex run_queue_mutex_;
+ std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
+};
+
+struct fdevent {
+ uint64_t id;
+
+ unique_fd fd;
+ int force_eof = 0;
+
+ uint16_t state = 0;
+ uint16_t events = 0;
+ std::optional<std::chrono::milliseconds> timeout;
+ std::chrono::steady_clock::time_point last_active;
+
+ std::variant<fd_func, fd_func2> func;
+ void* arg = nullptr;
+};
+
+// Backwards compatibility shims that forward to the global fdevent_context.
+fdevent* fdevent_create(int fd, fd_func func, void* arg);
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
+
+unique_fd fdevent_release(fdevent* fde);
+void fdevent_destroy(fdevent* fde);
+
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+void fdevent_loop();
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
+size_t fdevent_installed_count();
+void fdevent_reset();
+
+#endif
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
new file mode 100644
index 0000000..75ea081
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+#include "fdevent_poll.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <deque>
+#include <functional>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "sysdeps/chrono.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+ char buf[BUFSIZ];
+ ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, buf, sizeof(buf)));
+ if (rc == -1) {
+ PLOG(FATAL) << "failed to read from fdevent interrupt fd";
+ }
+}
+
+fdevent_context_poll::fdevent_context_poll() {
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create fdevent interrupt socketpair";
+ }
+
+ if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+ PLOG(FATAL) << "failed to make fdevent interrupt socket nonblocking";
+ }
+
+ this->interrupt_fd_.reset(s[0]);
+ fdevent* fde = this->Create(unique_fd(s[1]), fdevent_interrupt, nullptr);
+ CHECK(fde != nullptr);
+ this->Add(fde, FDE_READ);
+}
+
+fdevent_context_poll::~fdevent_context_poll() {
+ this->Destroy(this->interrupt_fde_);
+}
+
+void fdevent_context_poll::Register(fdevent* fde) {
+ auto pair = poll_node_map_.emplace(fde->fd.get(), PollNode(fde));
+ CHECK(pair.second) << "install existing fd " << fde->fd.get();
+}
+
+void fdevent_context_poll::Unregister(fdevent* fde) {
+ if (fde->state & FDE_ACTIVE) {
+ poll_node_map_.erase(fde->fd.get());
+
+ if (fde->state & FDE_PENDING) {
+ pending_list_.remove(fde);
+ }
+ fde->state = 0;
+ fde->events = 0;
+ }
+}
+
+void fdevent_context_poll::Set(fdevent* fde, unsigned events) {
+ CheckMainThread();
+ events &= FDE_EVENTMASK;
+ if ((fde->state & FDE_EVENTMASK) == events) {
+ return;
+ }
+ CHECK(fde->state & FDE_ACTIVE);
+
+ auto it = poll_node_map_.find(fde->fd.get());
+ CHECK(it != poll_node_map_.end());
+ PollNode& node = it->second;
+ if (events & FDE_READ) {
+ node.pollfd.events |= POLLIN;
+ } else {
+ node.pollfd.events &= ~POLLIN;
+ }
+
+ if (events & FDE_WRITE) {
+ node.pollfd.events |= POLLOUT;
+ } else {
+ node.pollfd.events &= ~POLLOUT;
+ }
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+ if (fde->state & FDE_PENDING) {
+ // If we are pending, make sure we don't signal an event that is no longer wanted.
+ fde->events &= events;
+ if (fde->events == 0) {
+ pending_list_.remove(fde);
+ fde->state &= ~FDE_PENDING;
+ }
+ }
+}
+
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
+ std::string result;
+ for (const auto& pollfd : pollfds) {
+ std::string op;
+ if (pollfd.events & POLLIN) {
+ op += "R";
+ }
+ if (pollfd.events & POLLOUT) {
+ op += "W";
+ }
+ android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+ }
+ return result;
+}
+
+static std::optional<std::chrono::milliseconds> calculate_timeout(fdevent_context_poll* ctx) {
+ std::optional<std::chrono::milliseconds> result = std::nullopt;
+ auto now = std::chrono::steady_clock::now();
+ ctx->CheckMainThread();
+
+ for (const auto& [fd, pollnode] : ctx->poll_node_map_) {
+ UNUSED(fd);
+ auto timeout_opt = pollnode.fde->timeout;
+ if (timeout_opt) {
+ auto deadline = pollnode.fde->last_active + *timeout_opt;
+ auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
+ if (time_left < std::chrono::milliseconds::zero()) {
+ time_left = std::chrono::milliseconds::zero();
+ }
+
+ if (!result) {
+ result = time_left;
+ } else {
+ result = std::min(*result, time_left);
+ }
+ }
+ }
+
+ return result;
+}
+
+static void fdevent_process(fdevent_context_poll* ctx) {
+ std::vector<adb_pollfd> pollfds;
+ for (const auto& pair : ctx->poll_node_map_) {
+ pollfds.push_back(pair.second.pollfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
+ auto timeout = calculate_timeout(ctx);
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
+ }
+
+ int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+
+ for (const auto& pollfd : pollfds) {
+ if (pollfd.revents != 0) {
+ D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
+ }
+ unsigned events = 0;
+ if (pollfd.revents & POLLIN) {
+ events |= FDE_READ;
+ }
+ if (pollfd.revents & POLLOUT) {
+ events |= FDE_WRITE;
+ }
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+#if defined(__linux__)
+ if (pollfd.revents & POLLRDHUP) {
+ events |= FDE_READ | FDE_ERROR;
+ }
+#endif
+ auto it = ctx->poll_node_map_.find(pollfd.fd);
+ CHECK(it != ctx->poll_node_map_.end());
+ fdevent* fde = it->second.fde;
+
+ if (events == 0) {
+ // Check for timeout.
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ CHECK_EQ(fde->fd.get(), pollfd.fd);
+ fde->events |= events;
+ fde->last_active = post_poll;
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ fde->state |= FDE_PENDING;
+ ctx->pending_list_.push_back(fde);
+ }
+ }
+}
+
+template <class T>
+struct always_false : std::false_type {};
+
+static void fdevent_call_fdfunc(fdevent* fde) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ CHECK(fde->state & FDE_PENDING);
+ fde->state &= (~FDE_PENDING);
+ D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
+ std::visit(
+ [&](auto&& f) {
+ using F = std::decay_t<decltype(f)>;
+ if constexpr (std::is_same_v<fd_func, F>) {
+ f(fde->fd.get(), events, fde->arg);
+ } else if constexpr (std::is_same_v<fd_func2, F>) {
+ f(fde, events, fde->arg);
+ } else {
+ static_assert(always_false<F>::value, "non-exhaustive visitor");
+ }
+ },
+ fde->func);
+}
+
+static void fdevent_check_spin(fdevent_context_poll* ctx, uint64_t cycle) {
+ // Check to see if we're spinning because we forgot about an fdevent
+ // by keeping track of how long fdevents have been continuously pending.
+ struct SpinCheck {
+ fdevent* fde;
+ android::base::boot_clock::time_point timestamp;
+ uint64_t cycle;
+ };
+
+ // TODO: Move this into the base fdevent_context.
+ static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+ static auto last_cycle = android::base::boot_clock::now();
+
+ auto now = android::base::boot_clock::now();
+ if (now - last_cycle > 10ms) {
+ // We're not spinning.
+ g_continuously_pending.clear();
+ last_cycle = now;
+ return;
+ }
+ last_cycle = now;
+
+ for (auto* fde : ctx->pending_list_) {
+ auto it = g_continuously_pending.find(fde->id);
+ if (it == g_continuously_pending.end()) {
+ g_continuously_pending[fde->id] =
+ SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+ } else {
+ it->second.cycle = cycle;
+ }
+ }
+
+ for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+ if (it->second.cycle != cycle) {
+ it = g_continuously_pending.erase(it);
+ } else {
+ // Use an absurdly long window, since all we really care about is
+ // getting a bugreport eventually.
+ if (now - it->second.timestamp > 300s) {
+ LOG(FATAL_WITHOUT_ABORT)
+ << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+ int fd = it->second.fde->fd.get();
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string path;
+ if (!android::base::Readlink(fd_path, &path)) {
+ PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+ }
+ LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+ abort();
+ }
+ ++it;
+ }
+ }
+}
+
+void fdevent_context_poll::Loop() {
+ main_thread_id_ = android::base::GetThreadId();
+
+ uint64_t cycle = 0;
+ while (true) {
+ if (terminate_loop_) {
+ break;
+ }
+
+ D("--- --- waiting for events");
+
+ fdevent_process(this);
+
+ fdevent_check_spin(this, cycle++);
+
+ while (!pending_list_.empty()) {
+ fdevent* fde = pending_list_.front();
+ pending_list_.pop_front();
+ fdevent_call_fdfunc(fde);
+ }
+
+ this->FlushRunQueue();
+ }
+
+ main_thread_id_.reset();
+}
+
+size_t fdevent_context_poll::InstalledCount() {
+ // We always have an installed fde for interrupt.
+ return poll_node_map_.size() - 1;
+}
+
+void fdevent_context_poll::Interrupt() {
+ int rc = adb_write(this->interrupt_fd_, "", 1);
+
+ // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+ if (rc == 0) {
+ PLOG(FATAL) << "fdevent interrupt fd was closed?";
+ } else if (rc == -1 && errno != EAGAIN) {
+ PLOG(FATAL) << "failed to write to fdevent interrupt fd";
+ }
+}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
new file mode 100644
index 0000000..db08301
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.h
@@ -0,0 +1,71 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+#include <deque>
+#include <list>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+struct PollNode {
+ fdevent* fde;
+ adb_pollfd pollfd;
+
+ explicit PollNode(fdevent* fde) : fde(fde) {
+ memset(&pollfd, 0, sizeof(pollfd));
+ pollfd.fd = fde->fd.get();
+
+#if defined(__linux__)
+ // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+ // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+ pollfd.events = POLLRDHUP;
+#endif
+ }
+};
+
+struct fdevent_context_poll : public fdevent_context {
+ fdevent_context_poll();
+ virtual ~fdevent_context_poll();
+
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
+ virtual void Set(fdevent* fde, unsigned events) final;
+
+ virtual void Loop() final;
+
+ virtual size_t InstalledCount() final;
+
+ protected:
+ virtual void Interrupt() final;
+
+ public:
+ // All operations to fdevent should happen only in the main thread.
+ // That's why we don't need a lock for fdevent.
+ std::unordered_map<int, PollNode> poll_node_map_;
+ std::list<fdevent*> pending_list_;
+
+ unique_fd interrupt_fd_;
+ fdevent* interrupt_fde_ = nullptr;
+};
diff --git a/adb/fdevent/fdevent_test.cpp b/adb/fdevent/fdevent_test.cpp
new file mode 100644
index 0000000..682f061
--- /dev/null
+++ b/adb/fdevent/fdevent_test.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <limits>
+#include <memory>
+#include <queue>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "adb_io.h"
+#include "fdevent_test.h"
+
+using namespace std::chrono_literals;
+
+class FdHandler {
+ public:
+ FdHandler(int read_fd, int write_fd, bool use_new_callback)
+ : read_fd_(read_fd), write_fd_(write_fd) {
+ if (use_new_callback) {
+ read_fde_ = fdevent_create(read_fd_, FdEventNewCallback, this);
+ write_fde_ = fdevent_create(write_fd_, FdEventNewCallback, this);
+ } else {
+ read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
+ write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
+ }
+ fdevent_add(read_fde_, FDE_READ);
+ }
+
+ ~FdHandler() {
+ fdevent_destroy(read_fde_);
+ fdevent_destroy(write_fde_);
+ }
+
+ private:
+ static void FdEventCallback(int fd, unsigned events, void* userdata) {
+ FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+ ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+ if (events & FDE_READ) {
+ ASSERT_EQ(fd, handler->read_fd_);
+ char c;
+ ASSERT_EQ(1, adb_read(fd, &c, 1));
+ handler->queue_.push(c);
+ fdevent_add(handler->write_fde_, FDE_WRITE);
+ }
+ if (events & FDE_WRITE) {
+ ASSERT_EQ(fd, handler->write_fd_);
+ ASSERT_FALSE(handler->queue_.empty());
+ char c = handler->queue_.front();
+ handler->queue_.pop();
+ ASSERT_EQ(1, adb_write(fd, &c, 1));
+ if (handler->queue_.empty()) {
+ fdevent_del(handler->write_fde_, FDE_WRITE);
+ }
+ }
+ }
+
+ static void FdEventNewCallback(fdevent* fde, unsigned events, void* userdata) {
+ int fd = fde->fd.get();
+ FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+ ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+ if (events & FDE_READ) {
+ ASSERT_EQ(fd, handler->read_fd_);
+ char c;
+ ASSERT_EQ(1, adb_read(fd, &c, 1));
+ handler->queue_.push(c);
+ fdevent_add(handler->write_fde_, FDE_WRITE);
+ }
+ if (events & FDE_WRITE) {
+ ASSERT_EQ(fd, handler->write_fd_);
+ ASSERT_FALSE(handler->queue_.empty());
+ char c = handler->queue_.front();
+ handler->queue_.pop();
+ ASSERT_EQ(1, adb_write(fd, &c, 1));
+ if (handler->queue_.empty()) {
+ fdevent_del(handler->write_fde_, FDE_WRITE);
+ }
+ }
+ }
+
+ private:
+ const int read_fd_;
+ const int write_fd_;
+ fdevent* read_fde_;
+ fdevent* write_fde_;
+ std::queue<char> queue_;
+};
+
+struct ThreadArg {
+ int first_read_fd;
+ int last_write_fd;
+ size_t middle_pipe_count;
+};
+
+TEST_F(FdeventTest, fdevent_terminate) {
+ PrepareThread();
+ TerminateThread();
+}
+
+TEST_F(FdeventTest, smoke) {
+ for (bool use_new_callback : {true, false}) {
+ fdevent_reset();
+ const size_t PIPE_COUNT = 10;
+ const size_t MESSAGE_LOOP_COUNT = 100;
+ const std::string MESSAGE = "fdevent_test";
+ int fd_pair1[2];
+ int fd_pair2[2];
+ ASSERT_EQ(0, adb_socketpair(fd_pair1));
+ ASSERT_EQ(0, adb_socketpair(fd_pair2));
+ ThreadArg thread_arg;
+ thread_arg.first_read_fd = fd_pair1[0];
+ thread_arg.last_write_fd = fd_pair2[1];
+ thread_arg.middle_pipe_count = PIPE_COUNT;
+ int writer = fd_pair1[1];
+ int reader = fd_pair2[0];
+
+ PrepareThread();
+
+ std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+ fdevent_run_on_main_thread([&thread_arg, &fd_handlers, use_new_callback]() {
+ std::vector<int> read_fds;
+ std::vector<int> write_fds;
+
+ read_fds.push_back(thread_arg.first_read_fd);
+ for (size_t i = 0; i < thread_arg.middle_pipe_count; ++i) {
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds));
+ read_fds.push_back(fds[0]);
+ write_fds.push_back(fds[1]);
+ }
+ write_fds.push_back(thread_arg.last_write_fd);
+
+ for (size_t i = 0; i < read_fds.size(); ++i) {
+ fd_handlers.push_back(
+ std::make_unique<FdHandler>(read_fds[i], write_fds[i], use_new_callback));
+ }
+ });
+ WaitForFdeventLoop();
+
+ for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+ std::string read_buffer = MESSAGE;
+ std::string write_buffer(MESSAGE.size(), 'a');
+ ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+ ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+ ASSERT_EQ(read_buffer, write_buffer);
+ }
+
+ fdevent_run_on_main_thread([&fd_handlers]() { fd_handlers.clear(); });
+ WaitForFdeventLoop();
+
+ TerminateThread();
+ ASSERT_EQ(0, adb_close(writer));
+ ASSERT_EQ(0, adb_close(reader));
+ }
+}
+
+struct InvalidFdArg {
+ fdevent* fde;
+ unsigned expected_events;
+ size_t* happened_event_count;
+};
+
+static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
+ InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
+ ASSERT_EQ(arg->expected_events, events);
+ fdevent_destroy(arg->fde);
+ if (++*(arg->happened_event_count) == 2) {
+ fdevent_terminate_loop();
+ }
+}
+
+static void InvalidFdThreadFunc() {
+ const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
+ size_t happened_event_count = 0;
+ InvalidFdArg read_arg;
+ read_arg.expected_events = FDE_READ | FDE_ERROR;
+ read_arg.happened_event_count = &happened_event_count;
+ read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
+ fdevent_add(read_arg.fde, FDE_READ);
+
+ const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
+ InvalidFdArg write_arg;
+ write_arg.expected_events = FDE_READ | FDE_ERROR;
+ write_arg.happened_event_count = &happened_event_count;
+ write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+ fdevent_add(write_arg.fde, FDE_WRITE);
+ fdevent_loop();
+}
+
+TEST_F(FdeventTest, invalid_fd) {
+ std::thread thread(InvalidFdThreadFunc);
+ thread.join();
+}
+
+TEST_F(FdeventTest, run_on_main_thread) {
+ std::vector<int> vec;
+
+ PrepareThread();
+
+ // Block the main thread for a long time while we queue our callbacks.
+ fdevent_run_on_main_thread([]() {
+ check_main_thread();
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ });
+
+ for (int i = 0; i < 1000000; ++i) {
+ fdevent_run_on_main_thread([i, &vec]() {
+ check_main_thread();
+ vec.push_back(i);
+ });
+ }
+
+ TerminateThread();
+
+ ASSERT_EQ(1000000u, vec.size());
+ for (int i = 0; i < 1000000; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
+}
+
+static std::function<void()> make_appender(std::vector<int>* vec, int value) {
+ return [vec, value]() {
+ check_main_thread();
+ if (value == 100) {
+ return;
+ }
+
+ vec->push_back(value);
+ fdevent_run_on_main_thread(make_appender(vec, value + 1));
+ };
+}
+
+TEST_F(FdeventTest, run_on_main_thread_reentrant) {
+ std::vector<int> vec;
+
+ PrepareThread();
+ fdevent_run_on_main_thread(make_appender(&vec, 0));
+ TerminateThread();
+
+ ASSERT_EQ(100u, vec.size());
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
+}
+
+TEST_F(FdeventTest, timeout) {
+ fdevent_reset();
+ PrepareThread();
+
+ enum class TimeoutEvent {
+ read,
+ timeout,
+ done,
+ };
+
+ struct TimeoutTest {
+ std::vector<std::pair<TimeoutEvent, std::chrono::steady_clock::time_point>> events;
+ fdevent* fde;
+ };
+ TimeoutTest test;
+
+ int fds[2];
+ ASSERT_EQ(0, adb_socketpair(fds));
+ static constexpr auto delta = 100ms;
+ fdevent_run_on_main_thread([&]() {
+ test.fde = fdevent_create(fds[0], [](fdevent* fde, unsigned events, void* arg) {
+ auto test = static_cast<TimeoutTest*>(arg);
+ auto now = std::chrono::steady_clock::now();
+ CHECK((events & FDE_READ) ^ (events & FDE_TIMEOUT));
+ TimeoutEvent event;
+ if ((events & FDE_READ)) {
+ char buf[2];
+ ssize_t rc = adb_read(fde->fd.get(), buf, sizeof(buf));
+ if (rc == 0) {
+ event = TimeoutEvent::done;
+ } else if (rc == 1) {
+ event = TimeoutEvent::read;
+ } else {
+ abort();
+ }
+ } else if ((events & FDE_TIMEOUT)) {
+ event = TimeoutEvent::timeout;
+ } else {
+ abort();
+ }
+
+ CHECK_EQ(fde, test->fde);
+ test->events.emplace_back(event, now);
+
+ if (event == TimeoutEvent::done) {
+ fdevent_destroy(fde);
+ }
+ }, &test);
+ fdevent_add(test.fde, FDE_READ);
+ fdevent_set_timeout(test.fde, delta);
+ });
+
+ ASSERT_EQ(1, adb_write(fds[1], "", 1));
+
+ // Timeout should happen here
+ std::this_thread::sleep_for(delta);
+
+ // and another.
+ std::this_thread::sleep_for(delta);
+
+ // No timeout should happen here.
+ std::this_thread::sleep_for(delta / 2);
+ adb_close(fds[1]);
+
+ TerminateThread();
+
+ ASSERT_EQ(4ULL, test.events.size());
+ ASSERT_EQ(TimeoutEvent::read, test.events[0].first);
+ ASSERT_EQ(TimeoutEvent::timeout, test.events[1].first);
+ ASSERT_EQ(TimeoutEvent::timeout, test.events[2].first);
+ ASSERT_EQ(TimeoutEvent::done, test.events[3].first);
+
+ std::vector<int> time_deltas;
+ for (size_t i = 0; i < test.events.size() - 1; ++i) {
+ auto before = test.events[i].second;
+ auto after = test.events[i + 1].second;
+ auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
+ time_deltas.push_back(diff.count());
+ }
+
+ std::vector<int> expected = {
+ delta.count(),
+ delta.count(),
+ delta.count() / 2,
+ };
+
+ std::vector<int> diff;
+ ASSERT_EQ(time_deltas.size(), expected.size());
+ for (size_t i = 0; i < time_deltas.size(); ++i) {
+ diff.push_back(std::abs(time_deltas[i] - expected[i]));
+ }
+
+ ASSERT_LT(diff[0], delta.count() * 0.5);
+ ASSERT_LT(diff[1], delta.count() * 0.5);
+ ASSERT_LT(diff[2], delta.count() * 0.5);
+}
diff --git a/adb/fdevent_test.h b/adb/fdevent/fdevent_test.h
similarity index 89%
rename from adb/fdevent_test.h
rename to adb/fdevent/fdevent_test.h
index 8d853c3..2139d0f 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -21,6 +21,7 @@
#include <thread>
#include "adb_io.h"
+#include "adb_unique_fd.h"
#include "socket.h"
#include "sysdeps.h"
#include "sysdeps/chrono.h"
@@ -45,7 +46,7 @@
class FdeventTest : public ::testing::Test {
protected:
- int dummy = -1;
+ unique_fd dummy;
static void SetUpTestCase() {
#if !defined(_WIN32)
@@ -65,27 +66,27 @@
FAIL() << "failed to create socketpair: " << strerror(errno);
}
- asocket* dummy_socket = create_local_socket(dummy_fds[1]);
+ asocket* dummy_socket = create_local_socket(unique_fd(dummy_fds[1]));
if (!dummy_socket) {
FAIL() << "failed to create local socket: " << strerror(errno);
}
dummy_socket->ready(dummy_socket);
- dummy = dummy_fds[0];
+ dummy.reset(dummy_fds[0]);
thread_ = std::thread([]() { fdevent_loop(); });
WaitForFdeventLoop();
}
size_t GetAdditionalLocalSocketCount() {
- // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
- return 2;
+ // dummy socket installed in PrepareThread()
+ return 1;
}
void TerminateThread() {
fdevent_terminate_loop();
ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
thread_.join();
- ASSERT_EQ(0, adb_close(dummy));
+ dummy.reset();
}
std::thread thread_;
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
deleted file mode 100644
index 816134f..0000000
--- a/adb/fdevent_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fdevent.h"
-
-#include <gtest/gtest.h>
-
-#include <limits>
-#include <memory>
-#include <queue>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include "adb_io.h"
-#include "fdevent_test.h"
-
-class FdHandler {
- public:
- FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
- read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
- fdevent_add(read_fde_, FDE_READ);
- write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
- }
-
- ~FdHandler() {
- fdevent_destroy(read_fde_);
- fdevent_destroy(write_fde_);
- }
-
- private:
- static void FdEventCallback(int fd, unsigned events, void* userdata) {
- FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
- ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
- if (events & FDE_READ) {
- ASSERT_EQ(fd, handler->read_fd_);
- char c;
- ASSERT_EQ(1, adb_read(fd, &c, 1));
- handler->queue_.push(c);
- fdevent_add(handler->write_fde_, FDE_WRITE);
- }
- if (events & FDE_WRITE) {
- ASSERT_EQ(fd, handler->write_fd_);
- ASSERT_FALSE(handler->queue_.empty());
- char c = handler->queue_.front();
- handler->queue_.pop();
- ASSERT_EQ(1, adb_write(fd, &c, 1));
- if (handler->queue_.empty()) {
- fdevent_del(handler->write_fde_, FDE_WRITE);
- }
- }
- }
-
- private:
- const int read_fd_;
- const int write_fd_;
- fdevent* read_fde_;
- fdevent* write_fde_;
- std::queue<char> queue_;
-};
-
-struct ThreadArg {
- int first_read_fd;
- int last_write_fd;
- size_t middle_pipe_count;
-};
-
-TEST_F(FdeventTest, fdevent_terminate) {
- PrepareThread();
- TerminateThread();
-}
-
-TEST_F(FdeventTest, smoke) {
- const size_t PIPE_COUNT = 10;
- const size_t MESSAGE_LOOP_COUNT = 100;
- const std::string MESSAGE = "fdevent_test";
- int fd_pair1[2];
- int fd_pair2[2];
- ASSERT_EQ(0, adb_socketpair(fd_pair1));
- ASSERT_EQ(0, adb_socketpair(fd_pair2));
- ThreadArg thread_arg;
- thread_arg.first_read_fd = fd_pair1[0];
- thread_arg.last_write_fd = fd_pair2[1];
- thread_arg.middle_pipe_count = PIPE_COUNT;
- int writer = fd_pair1[1];
- int reader = fd_pair2[0];
-
- PrepareThread();
-
- std::vector<std::unique_ptr<FdHandler>> fd_handlers;
- fdevent_run_on_main_thread([&thread_arg, &fd_handlers]() {
- std::vector<int> read_fds;
- std::vector<int> write_fds;
-
- read_fds.push_back(thread_arg.first_read_fd);
- for (size_t i = 0; i < thread_arg.middle_pipe_count; ++i) {
- int fds[2];
- ASSERT_EQ(0, adb_socketpair(fds));
- read_fds.push_back(fds[0]);
- write_fds.push_back(fds[1]);
- }
- write_fds.push_back(thread_arg.last_write_fd);
-
- for (size_t i = 0; i < read_fds.size(); ++i) {
- fd_handlers.push_back(std::make_unique<FdHandler>(read_fds[i], write_fds[i]));
- }
- });
- WaitForFdeventLoop();
-
- for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
- std::string read_buffer = MESSAGE;
- std::string write_buffer(MESSAGE.size(), 'a');
- ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
- ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
- ASSERT_EQ(read_buffer, write_buffer);
- }
-
- fdevent_run_on_main_thread([&fd_handlers]() { fd_handlers.clear(); });
- WaitForFdeventLoop();
-
- TerminateThread();
- ASSERT_EQ(0, adb_close(writer));
- ASSERT_EQ(0, adb_close(reader));
-}
-
-struct InvalidFdArg {
- fdevent* fde;
- unsigned expected_events;
- size_t* happened_event_count;
-};
-
-static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
- InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
- ASSERT_EQ(arg->expected_events, events);
- fdevent_destroy(arg->fde);
- if (++*(arg->happened_event_count) == 2) {
- fdevent_terminate_loop();
- }
-}
-
-static void InvalidFdThreadFunc() {
- const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
- size_t happened_event_count = 0;
- InvalidFdArg read_arg;
- read_arg.expected_events = FDE_READ | FDE_ERROR;
- read_arg.happened_event_count = &happened_event_count;
- read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
- fdevent_add(read_arg.fde, FDE_READ);
-
- const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
- InvalidFdArg write_arg;
- write_arg.expected_events = FDE_READ | FDE_ERROR;
- write_arg.happened_event_count = &happened_event_count;
- write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
- fdevent_add(write_arg.fde, FDE_WRITE);
- fdevent_loop();
-}
-
-TEST_F(FdeventTest, invalid_fd) {
- std::thread thread(InvalidFdThreadFunc);
- thread.join();
-}
-
-TEST_F(FdeventTest, run_on_main_thread) {
- std::vector<int> vec;
-
- PrepareThread();
-
- // Block the main thread for a long time while we queue our callbacks.
- fdevent_run_on_main_thread([]() {
- check_main_thread();
- std::this_thread::sleep_for(std::chrono::seconds(1));
- });
-
- for (int i = 0; i < 1000000; ++i) {
- fdevent_run_on_main_thread([i, &vec]() {
- check_main_thread();
- vec.push_back(i);
- });
- }
-
- TerminateThread();
-
- ASSERT_EQ(1000000u, vec.size());
- for (int i = 0; i < 1000000; ++i) {
- ASSERT_EQ(i, vec[i]);
- }
-}
-
-static std::function<void()> make_appender(std::vector<int>* vec, int value) {
- return [vec, value]() {
- check_main_thread();
- if (value == 100) {
- return;
- }
-
- vec->push_back(value);
- fdevent_run_on_main_thread(make_appender(vec, value + 1));
- };
-}
-
-TEST_F(FdeventTest, run_on_main_thread_reentrant) {
- std::vector<int> vec;
-
- PrepareThread();
- fdevent_run_on_main_thread(make_appender(&vec, 0));
- TerminateThread();
-
- ASSERT_EQ(100u, vec.size());
- for (int i = 0; i < 100; ++i) {
- ASSERT_EQ(i, vec[i]);
- }
-}
diff --git a/adb/services.cpp b/adb/services.cpp
index 4b033bd..6185aa6 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <thread>
+
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
@@ -63,31 +64,30 @@
adb_setsockopt(s[0], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
adb_setsockopt(s[1], SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
}
-#endif // !ADB_HOST
+#endif // !ADB_HOST
std::thread(service_bootstrap_func, service_name, func, unique_fd(s[1])).detach();
- D("service thread started, %d:%d",s[0], s[1]);
+ D("service thread started, %d:%d", s[0], s[1]);
return unique_fd(s[0]);
}
-int service_to_fd(const char* name, atransport* transport) {
- int ret = -1;
+unique_fd service_to_fd(std::string_view name, atransport* transport) {
+ unique_fd ret;
if (is_socket_spec(name)) {
std::string error;
- ret = socket_spec_connect(name, &error);
- if (ret < 0) {
+ if (!socket_spec_connect(&ret, name, nullptr, nullptr, &error)) {
LOG(ERROR) << "failed to connect to socket '" << name << "': " << error;
}
} else {
#if !ADB_HOST
- ret = daemon_service_to_fd(name, transport).release();
+ ret = daemon_service_to_fd(name, transport);
#endif
}
if (ret >= 0) {
- close_on_exec(ret);
+ close_on_exec(ret.get());
}
return ret;
}
@@ -100,9 +100,7 @@
ConnectionState state;
};
-static void wait_for_state(int fd, void* data) {
- std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
-
+static void wait_for_state(unique_fd fd, state_info* sinfo) {
D("wait_for_state %d", sinfo->state);
while (true) {
@@ -111,12 +109,21 @@
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : nullptr;
atransport* t = acquire_one_transport(sinfo->transport_type, serial, sinfo->transport_id,
&is_ambiguous, &error);
- if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
+ if (sinfo->state == kCsOffline) {
+ // wait-for-disconnect uses kCsOffline, we don't actually want to wait for 'offline'.
+ if (t == nullptr) {
+ SendOkay(fd);
+ break;
+ }
+ } else if (t != nullptr &&
+ (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
- } else if (!is_ambiguous) {
- adb_pollfd pfd = {.fd = fd, .events = POLLIN };
- int rc = adb_poll(&pfd, 1, 1000);
+ }
+
+ if (!is_ambiguous) {
+ adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
+ int rc = adb_poll(&pfd, 1, 100);
if (rc < 0) {
SendFail(fd, error);
break;
@@ -133,7 +140,6 @@
}
}
- adb_close(fd);
D("wait_for_state is done");
}
@@ -181,7 +187,7 @@
if (!strncmp(host.c_str(), "emu:", 4)) {
connect_emulator(host.c_str() + 4, &response);
} else {
- connect_device(host.c_str(), &response);
+ connect_device(host, &response);
}
// Send response for emulator and device
@@ -190,63 +196,58 @@
#endif
#if ADB_HOST
-asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
- if (!strcmp(name,"track-devices")) {
+asocket* host_service_to_socket(std::string_view name, std::string_view serial,
+ TransportId transport_id) {
+ if (name == "track-devices") {
return create_device_tracker(false);
- } else if (!strcmp(name, "track-devices-l")) {
+ } else if (name == "track-devices-l") {
return create_device_tracker(true);
- } else if (android::base::StartsWith(name, "wait-for-")) {
- name += strlen("wait-for-");
-
- std::unique_ptr<state_info> sinfo = std::make_unique<state_info>();
+ } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
+ std::shared_ptr<state_info> sinfo = std::make_shared<state_info>();
if (sinfo == nullptr) {
fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
return nullptr;
}
- if (serial) sinfo->serial = serial;
+ sinfo->serial = serial;
sinfo->transport_id = transport_id;
- if (android::base::StartsWith(name, "local")) {
- name += strlen("local");
+ if (android::base::ConsumePrefix(&name, "local")) {
sinfo->transport_type = kTransportLocal;
- } else if (android::base::StartsWith(name, "usb")) {
- name += strlen("usb");
+ } else if (android::base::ConsumePrefix(&name, "usb")) {
sinfo->transport_type = kTransportUsb;
- } else if (android::base::StartsWith(name, "any")) {
- name += strlen("any");
+ } else if (android::base::ConsumePrefix(&name, "any")) {
sinfo->transport_type = kTransportAny;
} else {
return nullptr;
}
- if (!strcmp(name, "-device")) {
+ if (name == "-device") {
sinfo->state = kCsDevice;
- } else if (!strcmp(name, "-recovery")) {
+ } else if (name == "-recovery") {
sinfo->state = kCsRecovery;
- } else if (!strcmp(name, "-sideload")) {
+ } else if (name == "-rescue") {
+ sinfo->state = kCsRescue;
+ } else if (name == "-sideload") {
sinfo->state = kCsSideload;
- } else if (!strcmp(name, "-bootloader")) {
+ } else if (name == "-bootloader") {
sinfo->state = kCsBootloader;
- } else if (!strcmp(name, "-any")) {
+ } else if (name == "-any") {
sinfo->state = kCsAny;
+ } else if (name == "-disconnect") {
+ sinfo->state = kCsOffline;
} else {
return nullptr;
}
- int fd = create_service_thread(
- "wait", std::bind(wait_for_state, std::placeholders::_1, sinfo.get()))
- .release();
- if (fd != -1) {
- sinfo.release();
- }
- return create_local_socket(fd);
- } else if (!strncmp(name, "connect:", 8)) {
- std::string host(name + strlen("connect:"));
- int fd = create_service_thread("connect",
- std::bind(connect_service, std::placeholders::_1, host))
- .release();
- return create_local_socket(fd);
+ unique_fd fd = create_service_thread(
+ "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
+ return create_local_socket(std::move(fd));
+ } else if (android::base::ConsumePrefix(&name, "connect:")) {
+ std::string host(name);
+ unique_fd fd = create_service_thread(
+ "connect", std::bind(connect_service, std::placeholders::_1, host));
+ return create_local_socket(std::move(fd));
}
return nullptr;
}
diff --git a/adb/services.h b/adb/services.h
index 0ce25ba..6fc89d7 100644
--- a/adb/services.h
+++ b/adb/services.h
@@ -23,5 +23,10 @@
constexpr char kShellServiceArgPty[] = "pty";
constexpr char kShellServiceArgShellProtocol[] = "v2";
+// Special flags sent by minadbd. They indicate the end of sideload transfer and the result of
+// installation or wipe.
+constexpr char kMinadbdServicesExitSuccess[] = "DONEDONE";
+constexpr char kMinadbdServicesExitFailure[] = "FAILFAIL";
+
unique_fd create_service_thread(const char* service_name, std::function<void(unique_fd)> func);
#endif // SERVICES_H_
diff --git a/adb/shell_protocol.h b/adb/shell_protocol.h
index 2c82689..4aab813 100644
--- a/adb/shell_protocol.h
+++ b/adb/shell_protocol.h
@@ -21,6 +21,7 @@
#include <android-base/macros.h>
#include "adb.h"
+#include "adb_unique_fd.h"
// Class to send and receive shell protocol packets.
//
@@ -60,7 +61,7 @@
// should be dynamically allocated on the heap instead.
//
// |fd| is an open file descriptor to be used to send or receive packets.
- explicit ShellProtocol(int fd);
+ explicit ShellProtocol(borrowed_fd fd);
virtual ~ShellProtocol();
// Returns a pointer to the data buffer.
@@ -103,7 +104,7 @@
kHeaderSize = sizeof(Id) + sizeof(length_t)
};
- int fd_;
+ borrowed_fd fd_;
char buffer_[kBufferSize];
size_t data_length_ = 0, bytes_left_ = 0;
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
index 13b66ec..95afaff 100644
--- a/adb/shell_service_protocol.cpp
+++ b/adb/shell_service_protocol.cpp
@@ -22,7 +22,7 @@
#include "adb_io.h"
-ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+ShellProtocol::ShellProtocol(borrowed_fd fd) : fd_(fd) {
buffer_[0] = kIdInvalid;
}
diff --git a/adb/socket.h b/adb/socket.h
index 0905aab..4276851 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -23,7 +23,8 @@
#include <memory>
#include <string>
-#include "fdevent.h"
+#include "adb_unique_fd.h"
+#include "fdevent/fdevent.h"
#include "types.h"
class atransport;
@@ -102,18 +103,19 @@
void remove_socket(asocket *s);
void close_all_sockets(atransport *t);
-asocket *create_local_socket(int fd);
-asocket* create_local_service_socket(const char* destination, atransport* transport);
+asocket* create_local_socket(unique_fd fd);
+asocket* create_local_service_socket(std::string_view destination, atransport* transport);
asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
+void connect_to_remote(asocket* s, std::string_view destination);
void connect_to_smartsocket(asocket *s);
// Internal functions that are only made available here for testing purposes.
namespace internal {
#if ADB_HOST
-char* skip_host_serial(char* service);
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+ std::string_view service);
#endif
} // namespace internal
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index eb4df97..98468b5 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -17,6 +17,7 @@
#include "socket_spec.h"
#include <string>
+#include <string_view>
#include <unordered_map>
#include <vector>
@@ -29,7 +30,8 @@
#include "adb.h"
#include "sysdeps.h"
-using android::base::StartsWith;
+using namespace std::string_literals;
+
using android::base::StringPrintf;
#if defined(__linux__)
@@ -44,6 +46,11 @@
#define ADB_WINDOWS 0
#endif
+#if ADB_LINUX
+#include <sys/socket.h>
+#include "sysdeps/vm_sockets.h"
+#endif
+
// Not static because it is used in commandline.c.
int gListenAll = 0;
@@ -64,10 +71,11 @@
{ "localfilesystem", { ANDROID_SOCKET_NAMESPACE_FILESYSTEM, !ADB_WINDOWS } },
});
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
- std::string* error) {
- if (!StartsWith(spec, "tcp:")) {
- *error = StringPrintf("specification is not tcp: '%s'", spec.c_str());
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
+ std::string* serial, std::string* error) {
+ if (!spec.starts_with("tcp:")) {
+ *error = "specification is not tcp: ";
+ *error += spec;
return false;
}
@@ -84,17 +92,18 @@
return false;
}
} else {
- std::string addr = spec.substr(4);
- port_value = -1;
+ std::string addr(spec.substr(4));
+ port_value = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
// FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
// on an address that isn't 'localhost' is unsupported.
- if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, nullptr, error)) {
+ if (!android::base::ParseNetAddress(addr, &hostname_value, &port_value, serial, error)) {
return false;
}
if (port_value == -1) {
- *error = StringPrintf("missing port in specification: '%s'", spec.c_str());
+ *error = "missing port in specification: ";
+ *error += spec;
return false;
}
}
@@ -110,87 +119,152 @@
return true;
}
-static bool tcp_host_is_local(const std::string& hostname) {
+static bool tcp_host_is_local(std::string_view hostname) {
// FIXME
return hostname.empty() || hostname == "localhost";
}
-bool is_socket_spec(const std::string& spec) {
+bool is_socket_spec(std::string_view spec) {
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
return true;
}
}
- return StartsWith(spec, "tcp:");
+ return spec.starts_with("tcp:");
}
-bool is_local_socket_spec(const std::string& spec) {
+bool is_local_socket_spec(std::string_view spec) {
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
return true;
}
}
std::string error;
std::string hostname;
- if (!parse_tcp_socket_spec(spec, &hostname, nullptr, &error)) {
+ if (!parse_tcp_socket_spec(spec, &hostname, nullptr, nullptr, &error)) {
return false;
}
return tcp_host_is_local(hostname);
}
-int socket_spec_connect(const std::string& spec, std::string* error) {
- if (StartsWith(spec, "tcp:")) {
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+ std::string* error) {
+ if (address.starts_with("tcp:")) {
std::string hostname;
- int port;
- if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
- return -1;
+ int port_value = port ? *port : 0;
+ if (!parse_tcp_socket_spec(address, &hostname, &port_value, serial, error)) {
+ return false;
}
- int result;
if (tcp_host_is_local(hostname)) {
- result = network_loopback_client(port, SOCK_STREAM, error);
+ fd->reset(network_loopback_client(port_value, SOCK_STREAM, error));
} else {
#if ADB_HOST
- result = network_connect(hostname, port, SOCK_STREAM, 0, error);
+ fd->reset(network_connect(hostname, port_value, SOCK_STREAM, 0, error));
#else
// Disallow arbitrary connections in adbd.
*error = "adbd does not support arbitrary tcp connections";
- return -1;
+ return false;
#endif
}
- if (result >= 0) {
- disable_tcp_nagle(result);
+ if (fd->get() > 0) {
+ disable_tcp_nagle(fd->get());
+ if (port) {
+ *port = port_value;
+ }
+ return true;
}
- return result;
+ return false;
+ } else if (address.starts_with("vsock:")) {
+#if ADB_LINUX
+ std::string spec_str(address);
+ std::vector<std::string> fragments = android::base::Split(spec_str, ":");
+ unsigned int port_value = port ? *port : 0;
+ if (fragments.size() != 2 && fragments.size() != 3) {
+ *error = android::base::StringPrintf("expected vsock:cid or vsock:port:cid in '%s'",
+ spec_str.c_str());
+ errno = EINVAL;
+ return false;
+ }
+ unsigned int cid = 0;
+ if (!android::base::ParseUint(fragments[1], &cid)) {
+ *error = android::base::StringPrintf("could not parse vsock cid in '%s'",
+ spec_str.c_str());
+ errno = EINVAL;
+ return false;
+ }
+ if (fragments.size() == 3 && !android::base::ParseUint(fragments[2], &port_value)) {
+ *error = android::base::StringPrintf("could not parse vsock port in '%s'",
+ spec_str.c_str());
+ errno = EINVAL;
+ return false;
+ }
+ if (port_value == 0) {
+ *error = android::base::StringPrintf("vsock port was not provided.");
+ errno = EINVAL;
+ return false;
+ }
+ fd->reset(socket(AF_VSOCK, SOCK_STREAM, 0));
+ if (fd->get() == -1) {
+ *error = "could not open vsock socket";
+ return false;
+ }
+ sockaddr_vm addr{};
+ addr.svm_family = AF_VSOCK;
+ addr.svm_port = port_value;
+ addr.svm_cid = cid;
+ if (serial) {
+ *serial = android::base::StringPrintf("vsock:%u:%d", cid, port_value);
+ }
+ if (connect(fd->get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
+ int error_num = errno;
+ *error = android::base::StringPrintf("could not connect to vsock address '%s'",
+ spec_str.c_str());
+ errno = error_num;
+ return false;
+ }
+ if (port) {
+ *port = port_value;
+ }
+ return true;
+#else // ADB_LINUX
+ *error = "vsock is only supported on linux";
+ return false;
+#endif // ADB_LINUX
}
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (address.starts_with(prefix)) {
if (!it.second.available) {
*error = StringPrintf("socket type %s is unavailable on this platform",
it.first.c_str());
- return -1;
+ return false;
}
- return network_local_client(&spec[prefix.length()], it.second.socket_namespace,
- SOCK_STREAM, error);
+ fd->reset(network_local_client(&address[prefix.length()], it.second.socket_namespace,
+ SOCK_STREAM, error));
+ if (serial) {
+ *serial = address;
+ }
+ return true;
}
}
- *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
- return -1;
+ *error = "unknown socket specification: ";
+ *error += address;
+ return false;
}
-int socket_spec_listen(const std::string& spec, std::string* error, int* resolved_tcp_port) {
- if (StartsWith(spec, "tcp:")) {
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_port) {
+ if (spec.starts_with("tcp:")) {
std::string hostname;
int port;
- if (!parse_tcp_socket_spec(spec, &hostname, &port, error)) {
+ if (!parse_tcp_socket_spec(spec, &hostname, &port, nullptr, error)) {
return -1;
}
@@ -205,18 +279,67 @@
return -1;
}
- if (result >= 0 && port == 0 && resolved_tcp_port) {
- *resolved_tcp_port = adb_socket_get_local_port(result);
+ if (result >= 0 && resolved_port) {
+ *resolved_port = adb_socket_get_local_port(result);
}
return result;
+ } else if (spec.starts_with("vsock:")) {
+#if ADB_LINUX
+ std::string spec_str(spec);
+ std::vector<std::string> fragments = android::base::Split(spec_str, ":");
+ if (fragments.size() != 2) {
+ *error = "given vsock server socket string was invalid";
+ return -1;
+ }
+ int port;
+ if (!android::base::ParseInt(fragments[1], &port)) {
+ *error = "could not parse vsock port";
+ errno = EINVAL;
+ return -1;
+ } else if (port < 0) {
+ *error = "vsock port was negative.";
+ errno = EINVAL;
+ return -1;
+ }
+ unique_fd serverfd(socket(AF_VSOCK, SOCK_STREAM, 0));
+ if (serverfd == -1) {
+ int error_num = errno;
+ *error = android::base::StringPrintf("could not create vsock server: '%s'",
+ strerror(error_num));
+ errno = error_num;
+ return -1;
+ }
+ sockaddr_vm addr{};
+ addr.svm_family = AF_VSOCK;
+ addr.svm_port = port == 0 ? VMADDR_PORT_ANY : port;
+ addr.svm_cid = VMADDR_CID_ANY;
+ socklen_t addr_len = sizeof(addr);
+ if (bind(serverfd.get(), reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
+ return -1;
+ }
+ if (listen(serverfd.get(), 4)) {
+ return -1;
+ }
+ if (serverfd >= 0 && resolved_port) {
+ if (getsockname(serverfd.get(), reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
+ *resolved_port = addr.svm_port;
+ } else {
+ return -1;
+ }
+ }
+ return serverfd.release();
+#else // ADB_LINUX
+ *error = "vsock is only supported on linux";
+ return -1;
+#endif // ADB_LINUX
}
for (const auto& it : kLocalSocketTypes) {
std::string prefix = it.first + ":";
- if (StartsWith(spec, prefix)) {
+ if (spec.starts_with(prefix)) {
if (!it.second.available) {
- *error = StringPrintf("attempted to listen on unavailable socket type: '%s'",
- spec.c_str());
+ *error = "attempted to listen on unavailable socket type: ";
+ *error += spec;
return -1;
}
@@ -225,6 +348,7 @@
}
}
- *error = StringPrintf("unknown socket specification '%s'", spec.c_str());
+ *error = "unknown socket specification:";
+ *error += spec;
return -1;
}
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 6920e91..7cc2fac 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -17,15 +17,17 @@
#pragma once
#include <string>
+#include <tuple>
+
+#include "adb_unique_fd.h"
// Returns true if the argument starts with a plausible socket prefix.
-bool is_socket_spec(const std::string& spec);
-bool is_local_socket_spec(const std::string& spec);
+bool is_socket_spec(std::string_view spec);
+bool is_local_socket_spec(std::string_view spec);
-int socket_spec_connect(const std::string& spec, std::string* error);
-int socket_spec_listen(const std::string& spec, std::string* error,
- int* resolved_tcp_port = nullptr);
+bool socket_spec_connect(unique_fd* fd, std::string_view address, int* port, std::string* serial,
+ std::string* error);
+int socket_spec_listen(std::string_view spec, std::string* error, int* resolved_tcp_port = nullptr);
-// Exposed for testing.
-bool parse_tcp_socket_spec(const std::string& spec, std::string* hostname, int* port,
- std::string* error);
+bool parse_tcp_socket_spec(std::string_view spec, std::string* hostname, int* port,
+ std::string* serial, std::string* error);
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 40ce21c..3a2f60c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -20,35 +20,71 @@
#include <gtest/gtest.h>
-TEST(socket_spec, parse_tcp_socket_spec) {
- std::string hostname, error;
+TEST(socket_spec, parse_tcp_socket_spec_just_port) {
+ std::string hostname, error, serial;
int port;
- EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &error));
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
EXPECT_EQ("", hostname);
EXPECT_EQ(5037, port);
+ EXPECT_EQ("", serial);
+}
- // Bad ports:
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &error));
+TEST(socket_spec, parse_tcp_socket_spec_bad_ports) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
+}
- EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &error));
+TEST(socket_spec, parse_tcp_socket_spec_host_and_port) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
EXPECT_EQ("localhost", hostname);
EXPECT_EQ(1234, port);
+ EXPECT_EQ("localhost:1234", serial);
+}
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &error));
+TEST(socket_spec, parse_tcp_socket_spec_host_no_port) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+ EXPECT_EQ("localhost", hostname);
+ EXPECT_EQ(5555, port);
+ EXPECT_EQ("localhost:5555", serial);
+}
- // IPv6:
- EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &error));
+TEST(socket_spec, parse_tcp_socket_spec_host_bad_ports) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_and_port) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
EXPECT_EQ("::1", hostname);
EXPECT_EQ(1234, port);
+ EXPECT_EQ("[::1]:1234", serial);
+}
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &error));
- EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &error));
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_no_port) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_TRUE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+ EXPECT_EQ("::1", hostname);
+ EXPECT_EQ(5555, port);
+ EXPECT_EQ("[::1]:5555", serial);
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_bad_ports) {
+ std::string hostname, error, serial;
+ int port;
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
+ EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
}
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04214a2..1601ff0 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include <gtest/gtest.h>
@@ -29,11 +29,14 @@
#include "adb.h"
#include "adb_io.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
#include "socket.h"
#include "sysdeps.h"
#include "sysdeps/chrono.h"
+using namespace std::string_literals;
+using namespace std::string_view_literals;
+
struct ThreadArg {
int first_read_fd;
int last_write_fd;
@@ -55,7 +58,7 @@
intermediates.resize(INTERMEDIATE_COUNT);
ASSERT_EQ(0, adb_socketpair(first)) << strerror(errno);
ASSERT_EQ(0, adb_socketpair(last)) << strerror(errno);
- asocket* prev_tail = create_local_socket(first[1]);
+ asocket* prev_tail = create_local_socket(unique_fd(first[1]));
ASSERT_NE(nullptr, prev_tail);
auto connect = [](asocket* tail, asocket* head) {
@@ -67,17 +70,17 @@
for (auto& intermediate : intermediates) {
ASSERT_EQ(0, adb_socketpair(intermediate.data())) << strerror(errno);
- asocket* head = create_local_socket(intermediate[0]);
+ asocket* head = create_local_socket(unique_fd(intermediate[0]));
ASSERT_NE(nullptr, head);
- asocket* tail = create_local_socket(intermediate[1]);
+ asocket* tail = create_local_socket(unique_fd(intermediate[1]));
ASSERT_NE(nullptr, tail);
connect(prev_tail, head);
prev_tail = tail;
}
- asocket* end = create_local_socket(last[0]);
+ asocket* end = create_local_socket(unique_fd(last[0]));
ASSERT_NE(nullptr, end);
connect(prev_tail, end);
@@ -101,14 +104,14 @@
}
struct CloseWithPacketArg {
- int socket_fd;
+ unique_fd socket_fd;
size_t bytes_written;
- int cause_close_fd;
+ unique_fd cause_close_fd;
};
static void CreateCloser(CloseWithPacketArg* arg) {
fdevent_run_on_main_thread([arg]() {
- asocket* s = create_local_socket(arg->socket_fd);
+ asocket* s = create_local_socket(std::move(arg->socket_fd));
ASSERT_TRUE(s != nullptr);
arg->bytes_written = 0;
@@ -132,7 +135,7 @@
}
ASSERT_TRUE(socket_filled);
- asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
+ asocket* cause_close_s = create_local_socket(std::move(arg->cause_close_fd));
ASSERT_TRUE(cause_close_s != nullptr);
cause_close_s->peer = s;
s->peer = cause_close_s;
@@ -151,8 +154,8 @@
int cause_close_fd[2];
ASSERT_EQ(0, adb_socketpair(cause_close_fd));
CloseWithPacketArg arg;
- arg.socket_fd = socket_fd[1];
- arg.cause_close_fd = cause_close_fd[1];
+ arg.socket_fd.reset(socket_fd[1]);
+ arg.cause_close_fd.reset(cause_close_fd[1]);
PrepareThread();
CreateCloser(&arg);
@@ -175,8 +178,8 @@
int cause_close_fd[2];
ASSERT_EQ(0, adb_socketpair(cause_close_fd));
CloseWithPacketArg arg;
- arg.socket_fd = socket_fd[1];
- arg.cause_close_fd = cause_close_fd[1];
+ arg.socket_fd.reset(socket_fd[1]);
+ arg.cause_close_fd.reset(cause_close_fd[1]);
PrepareThread();
CreateCloser(&arg);
@@ -208,8 +211,8 @@
int cause_close_fd[2];
ASSERT_EQ(0, adb_socketpair(cause_close_fd));
CloseWithPacketArg arg;
- arg.socket_fd = socket_fd[1];
- arg.cause_close_fd = cause_close_fd[1];
+ arg.socket_fd.reset(socket_fd[1]);
+ arg.cause_close_fd.reset(cause_close_fd[1]);
PrepareThread();
CreateCloser(&arg);
@@ -218,6 +221,8 @@
EXPECT_EQ(2u + GetAdditionalLocalSocketCount(), fdevent_installed_count());
ASSERT_EQ(0, adb_close(socket_fd[0]));
+ std::this_thread::sleep_for(2s);
+
WaitForFdeventLoop();
ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
TerminateThread();
@@ -230,8 +235,8 @@
ASSERT_EQ(0, adb_socketpair(head_fd));
ASSERT_EQ(0, adb_socketpair(tail_fd));
- asocket* head = create_local_socket(head_fd[1]);
- asocket* tail = create_local_socket(tail_fd[1]);
+ asocket* head = create_local_socket(unique_fd(head_fd[1]));
+ asocket* tail = create_local_socket(unique_fd(tail_fd[1]));
head->peer = tail;
head->ready(head);
@@ -284,7 +289,7 @@
PrepareThread();
fdevent_run_on_main_thread([accept_fd]() {
- asocket* s = create_local_socket(accept_fd);
+ asocket* s = create_local_socket(unique_fd(accept_fd));
ASSERT_TRUE(s != nullptr);
});
@@ -303,56 +308,78 @@
#if ADB_HOST
-// Checks that skip_host_serial(serial) returns a pointer to the part of |serial| which matches
-// |expected|, otherwise logs the failure to gtest.
-void VerifySkipHostSerial(std::string serial, const char* expected) {
- char* result = internal::skip_host_serial(&serial[0]);
- if (expected == nullptr) {
- EXPECT_EQ(nullptr, result);
- } else {
- EXPECT_STREQ(expected, result);
- }
-}
+#define VerifyParseHostServiceFailed(s) \
+ do { \
+ std::string service(s); \
+ std::string_view serial, command; \
+ bool result = internal::parse_host_service(&serial, &command, service); \
+ EXPECT_FALSE(result); \
+ } while (0)
+
+#define VerifyParseHostService(s, expected_serial, expected_command) \
+ do { \
+ std::string service(s); \
+ std::string_view serial, command; \
+ bool result = internal::parse_host_service(&serial, &command, service); \
+ EXPECT_TRUE(result); \
+ EXPECT_EQ(std::string(expected_serial), std::string(serial)); \
+ EXPECT_EQ(std::string(expected_command), std::string(command)); \
+ } while (0);
// Check [tcp:|udp:]<serial>[:<port>]:<command> format.
-TEST(socket_test, test_skip_host_serial) {
+TEST(socket_test, test_parse_host_service) {
for (const std::string& protocol : {"", "tcp:", "udp:"}) {
- VerifySkipHostSerial(protocol, nullptr);
- VerifySkipHostSerial(protocol + "foo", nullptr);
+ VerifyParseHostServiceFailed(protocol);
+ VerifyParseHostServiceFailed(protocol + "foo");
- VerifySkipHostSerial(protocol + "foo:bar", ":bar");
- VerifySkipHostSerial(protocol + "foo:bar:baz", ":bar:baz");
+ {
+ std::string serial = protocol + "foo";
+ VerifyParseHostService(serial + ":bar", serial, "bar");
+ VerifyParseHostService(serial + " :bar:baz", serial, "bar:baz");
+ }
- VerifySkipHostSerial(protocol + "foo:123:bar", ":bar");
- VerifySkipHostSerial(protocol + "foo:123:456", ":456");
- VerifySkipHostSerial(protocol + "foo:123:bar:baz", ":bar:baz");
+ {
+ // With port.
+ std::string serial = protocol + "foo:123";
+ VerifyParseHostService(serial + ":bar", serial, "bar");
+ VerifyParseHostService(serial + ":456", serial, "456");
+ VerifyParseHostService(serial + ":bar:baz", serial, "bar:baz");
+ }
// Don't register a port unless it's all numbers and ends with ':'.
- VerifySkipHostSerial(protocol + "foo:123", ":123");
- VerifySkipHostSerial(protocol + "foo:123bar:baz", ":123bar:baz");
+ VerifyParseHostService(protocol + "foo:123", protocol + "foo", "123");
+ VerifyParseHostService(protocol + "foo:123bar:baz", protocol + "foo", "123bar:baz");
- VerifySkipHostSerial(protocol + "100.100.100.100:5555:foo", ":foo");
- VerifySkipHostSerial(protocol + "[0123:4567:89ab:CDEF:0:9:a:f]:5555:foo", ":foo");
- VerifySkipHostSerial(protocol + "[::1]:5555:foo", ":foo");
+ std::string addresses[] = {"100.100.100.100", "[0123:4567:89ab:CDEF:0:9:a:f]", "[::1]"};
+ for (const std::string& address : addresses) {
+ std::string serial = protocol + address;
+ std::string serial_with_port = protocol + address + ":5555";
+ VerifyParseHostService(serial + ":foo", serial, "foo");
+ VerifyParseHostService(serial_with_port + ":foo", serial_with_port, "foo");
+ }
// If we can't find both [] then treat it as a normal serial with [ in it.
- VerifySkipHostSerial(protocol + "[0123:foo", ":foo");
+ VerifyParseHostService(protocol + "[0123:foo", protocol + "[0123", "foo");
// Don't be fooled by random IPv6 addresses in the command string.
- VerifySkipHostSerial(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
- ":ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+ VerifyParseHostService(protocol + "foo:ping [0123:4567:89ab:CDEF:0:9:a:f]:5555",
+ protocol + "foo", "ping [0123:4567:89ab:CDEF:0:9:a:f]:5555");
+
+ // Handle embedded NULs properly.
+ VerifyParseHostService(protocol + "foo:echo foo\0bar"s, protocol + "foo",
+ "echo foo\0bar"sv);
}
}
// Check <prefix>:<serial>:<command> format.
-TEST(socket_test, test_skip_host_serial_prefix) {
+TEST(socket_test, test_parse_host_service_prefix) {
for (const std::string& prefix : {"usb:", "product:", "model:", "device:"}) {
- VerifySkipHostSerial(prefix, nullptr);
- VerifySkipHostSerial(prefix + "foo", nullptr);
+ VerifyParseHostServiceFailed(prefix);
+ VerifyParseHostServiceFailed(prefix + "foo");
- VerifySkipHostSerial(prefix + "foo:bar", ":bar");
- VerifySkipHostSerial(prefix + "foo:bar:baz", ":bar:baz");
- VerifySkipHostSerial(prefix + "foo:123:bar", ":123:bar");
+ VerifyParseHostService(prefix + "foo:bar", prefix + "foo", "bar");
+ VerifyParseHostService(prefix + "foo:bar:baz", prefix + "foo", "bar:baz");
+ VerifyParseHostService(prefix + "foo:123:bar", prefix + "foo", "123:bar");
}
}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8b07f74..75993b3 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -26,10 +26,13 @@
#include <unistd.h>
#include <algorithm>
+#include <chrono>
#include <mutex>
#include <string>
#include <vector>
+#include <android-base/strings.h>
+
#if !ADB_HOST
#include <android-base/properties.h>
#include <log/log_properties.h>
@@ -37,9 +40,12 @@
#include "adb.h"
#include "adb_io.h"
+#include "adb_utils.h"
#include "transport.h"
#include "types.h"
+using namespace std::chrono_literals;
+
static std::recursive_mutex& local_socket_list_lock = *new std::recursive_mutex();
static unsigned local_socket_next_id = 1;
@@ -237,16 +243,64 @@
fdevent_add(s->fde, FDE_READ);
}
+struct ClosingSocket {
+ std::chrono::steady_clock::time_point begin;
+};
+
+// The standard (RFC 1122 - 4.2.2.13) says that if we call close on a
+// socket while we have pending data, a TCP RST should be sent to the
+// other end to notify it that we didn't read all of its data. However,
+// this can result in data that we've successfully written out to be dropped
+// on the other end. To avoid this, instead of immediately closing a
+// socket, call shutdown on it instead, and then read from the file
+// descriptor until we hit EOF or an error before closing.
+static void deferred_close(unique_fd fd) {
+ // Shutdown the socket in the outgoing direction only, so that
+ // we don't have the same problem on the opposite end.
+ adb_shutdown(fd.get(), SHUT_WR);
+ auto callback = [](fdevent* fde, unsigned event, void* arg) {
+ auto socket_info = static_cast<ClosingSocket*>(arg);
+ if (event & FDE_READ) {
+ ssize_t rc;
+ char buf[BUFSIZ];
+ while ((rc = adb_read(fde->fd.get(), buf, sizeof(buf))) > 0) {
+ continue;
+ }
+
+ if (rc == -1 && errno == EAGAIN) {
+ // There's potentially more data to read.
+ auto duration = std::chrono::steady_clock::now() - socket_info->begin;
+ if (duration > 1s) {
+ LOG(WARNING) << "timeout expired while flushing socket, closing";
+ } else {
+ return;
+ }
+ }
+ } else if (event & FDE_TIMEOUT) {
+ LOG(WARNING) << "timeout expired while flushing socket, closing";
+ }
+
+ // Either there was an error, we hit the end of the socket, or our timeout expired.
+ fdevent_destroy(fde);
+ delete socket_info;
+ };
+
+ ClosingSocket* socket_info = new ClosingSocket{
+ .begin = std::chrono::steady_clock::now(),
+ };
+
+ fdevent* fde = fdevent_create(fd.release(), callback, socket_info);
+ fdevent_add(fde, FDE_READ);
+ fdevent_set_timeout(fde, 1s);
+}
+
// be sure to hold the socket list lock when calling this
static void local_socket_destroy(asocket* s) {
int exit_on_close = s->exit_on_close;
D("LS(%d): destroying fde.fd=%d", s->id, s->fd);
- /* IMPORTANT: the remove closes the fd
- ** that belongs to this socket
- */
- fdevent_destroy(s->fde);
+ deferred_close(fdevent_release(s->fde));
remove_socket(s);
delete s;
@@ -332,7 +386,8 @@
}
}
-asocket* create_local_socket(int fd) {
+asocket* create_local_socket(unique_fd ufd) {
+ int fd = ufd.release();
asocket* s = new asocket();
s->fd = fd;
s->enqueue = local_socket_enqueue;
@@ -346,28 +401,25 @@
return s;
}
-asocket* create_local_service_socket(const char* name, atransport* transport) {
+asocket* create_local_service_socket(std::string_view name, atransport* transport) {
#if !ADB_HOST
- if (!strcmp(name, "jdwp")) {
- return create_jdwp_service_socket();
- }
- if (!strcmp(name, "track-jdwp")) {
- return create_jdwp_tracker_service_socket();
+ if (asocket* s = daemon_service_to_socket(name); s) {
+ return s;
}
#endif
- int fd = service_to_fd(name, transport);
+ unique_fd fd = service_to_fd(name, transport);
if (fd < 0) {
return nullptr;
}
- asocket* s = create_local_socket(fd);
- D("LS(%d): bound to '%s' via %d", s->id, name, fd);
+ int fd_value = fd.get();
+ asocket* s = create_local_socket(std::move(fd));
+ LOG(VERBOSE) << "LS(" << s->id << "): bound to '" << name << "' via " << fd_value;
#if !ADB_HOST
- if ((!strncmp(name, "root:", 5) && getuid() != 0 && __android_log_is_debuggable()) ||
- (!strncmp(name, "unroot:", 7) && getuid() == 0) ||
- !strncmp(name, "usb:", 4) ||
- !strncmp(name, "tcpip:", 6)) {
+ if ((name.starts_with("root:") && getuid() != 0 && __android_log_is_debuggable()) ||
+ (name.starts_with("unroot:") && getuid() == 0) || name.starts_with("usb:") ||
+ name.starts_with("tcpip:")) {
D("LS(%d): enabling exit_on_close", s->id);
s->exit_on_close = 1;
}
@@ -376,22 +428,6 @@
return s;
}
-#if ADB_HOST
-static asocket* create_host_service_socket(const char* name, const char* serial,
- TransportId transport_id) {
- asocket* s;
-
- s = host_service_to_socket(name, serial, transport_id);
-
- if (s != nullptr) {
- D("LS(%d) bound to '%s'", s->id, name);
- return s;
- }
-
- return s;
-}
-#endif /* ADB_HOST */
-
static int remote_socket_enqueue(asocket* s, apacket::payload_type data) {
D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d", s->id, s->fd, s->peer->fd);
apacket* p = get_apacket();
@@ -465,16 +501,19 @@
return s;
}
-void connect_to_remote(asocket* s, const char* destination) {
+void connect_to_remote(asocket* s, std::string_view destination) {
D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
apacket* p = get_apacket();
- D("LS(%d): connect('%s')", s->id, destination);
+ LOG(VERBOSE) << "LS(" << s->id << ": connect(" << destination << ")";
p->msg.command = A_OPEN;
p->msg.arg0 = s->id;
- // adbd expects a null-terminated string.
- p->payload.assign(destination, destination + strlen(destination) + 1);
+ // adbd used to expect a null-terminated string.
+ // Keep doing so to maintain backward compatibility.
+ p->payload.resize(destination.size() + 1);
+ memcpy(p->payload.data(), destination.data(), destination.size());
+ p->payload[destination.size()] = '\0';
p->msg.data_length = p->payload.size();
CHECK_LE(p->msg.data_length, s->get_max_payload());
@@ -550,57 +589,127 @@
namespace internal {
-// Returns the position in |service| following the target serial parameter. Serial format can be
-// any of:
+// Parses a host service string of the following format:
// * [tcp:|udp:]<serial>[:<port>]:<command>
// * <prefix>:<serial>:<command>
// Where <port> must be a base-10 number and <prefix> may be any of {usb,product,model,device}.
-//
-// The returned pointer will point to the ':' just before <command>, or nullptr if not found.
-char* skip_host_serial(char* service) {
- static const std::vector<std::string>& prefixes =
- *(new std::vector<std::string>{"usb:", "product:", "model:", "device:"});
+bool parse_host_service(std::string_view* out_serial, std::string_view* out_command,
+ std::string_view full_service) {
+ if (full_service.empty()) {
+ return false;
+ }
- for (const std::string& prefix : prefixes) {
- if (!strncmp(service, prefix.c_str(), prefix.length())) {
- return strchr(service + prefix.length(), ':');
+ std::string_view serial;
+ std::string_view command = full_service;
+ // Remove |count| bytes from the beginning of command and add them to |serial|.
+ auto consume = [&full_service, &serial, &command](size_t count) {
+ CHECK_LE(count, command.size());
+ if (!serial.empty()) {
+ CHECK_EQ(serial.data() + serial.size(), command.data());
+ }
+
+ serial = full_service.substr(0, serial.size() + count);
+ command.remove_prefix(count);
+ };
+
+ // Remove the trailing : from serial, and assign the values to the output parameters.
+ auto finish = [out_serial, out_command, &serial, &command] {
+ if (serial.empty() || command.empty()) {
+ return false;
+ }
+
+ CHECK_EQ(':', serial.back());
+ serial.remove_suffix(1);
+
+ *out_serial = serial;
+ *out_command = command;
+ return true;
+ };
+
+ static constexpr std::string_view prefixes[] = {"usb:", "product:", "model:", "device:"};
+ for (std::string_view prefix : prefixes) {
+ if (command.starts_with(prefix)) {
+ consume(prefix.size());
+
+ size_t offset = command.find_first_of(':');
+ if (offset == std::string::npos) {
+ return false;
+ }
+ consume(offset + 1);
+ return finish();
}
}
// For fastboot compatibility, ignore protocol prefixes.
- if (!strncmp(service, "tcp:", 4) || !strncmp(service, "udp:", 4)) {
- service += 4;
+ if (command.starts_with("tcp:") || command.starts_with("udp:")) {
+ consume(4);
+ if (command.empty()) {
+ return false;
+ }
+ }
+ if (command.starts_with("vsock:")) {
+ // vsock serials are vsock:cid:port, which have an extra colon compared to tcp.
+ size_t next_colon = command.find(':');
+ if (next_colon == std::string::npos) {
+ return false;
+ }
+ consume(next_colon + 1);
}
- // Check for an IPv6 address. `adb connect` creates the serial number from the canonical
- // network address so it will always have the [] delimiters.
- if (service[0] == '[') {
- char* ipv6_end = strchr(service, ']');
- if (ipv6_end != nullptr) {
- service = ipv6_end;
+ bool found_address = false;
+ if (command[0] == '[') {
+ // Read an IPv6 address. `adb connect` creates the serial number from the canonical
+ // network address so it will always have the [] delimiters.
+ size_t ipv6_end = command.find_first_of(']');
+ if (ipv6_end != std::string::npos) {
+ consume(ipv6_end + 1);
+ if (command.empty()) {
+ // Nothing after the IPv6 address.
+ return false;
+ } else if (command[0] != ':') {
+ // Garbage after the IPv6 address.
+ return false;
+ }
+ consume(1);
+ found_address = true;
}
}
- // The next colon we find must either begin the port field or the command field.
- char* colon_ptr = strchr(service, ':');
- if (!colon_ptr) {
- // No colon in service string.
- return nullptr;
+ if (!found_address) {
+ // Scan ahead to the next colon.
+ size_t offset = command.find_first_of(':');
+ if (offset == std::string::npos) {
+ return false;
+ }
+ consume(offset + 1);
}
- // If the next field is only decimal digits and ends with another colon, it's a port.
- char* serial_end = colon_ptr;
- if (isdigit(serial_end[1])) {
- serial_end++;
- while (*serial_end && isdigit(*serial_end)) {
- serial_end++;
- }
- if (*serial_end != ':') {
- // Something other than "<port>:" was found, this must be the command field instead.
- serial_end = colon_ptr;
+ // We're either at the beginning of a port, or the command itself.
+ // Look for a port in between colons.
+ size_t next_colon = command.find_first_of(':');
+ if (next_colon == std::string::npos) {
+ // No colon, we must be at the command.
+ return finish();
+ }
+
+ bool port_valid = true;
+ if (command.size() <= next_colon) {
+ return false;
+ }
+
+ std::string_view port = command.substr(0, next_colon);
+ for (auto digit : port) {
+ if (!isdigit(digit)) {
+ // Port isn't a number.
+ port_valid = false;
+ break;
}
}
- return serial_end;
+
+ if (port_valid) {
+ consume(next_colon + 1);
+ }
+ return finish();
}
} // namespace internal
@@ -609,8 +718,8 @@
static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
#if ADB_HOST
- char* service = nullptr;
- char* serial = nullptr;
+ std::string_view service;
+ std::string_view serial;
TransportId transport_id = 0;
TransportType type = kTransportAny;
#endif
@@ -647,61 +756,63 @@
D("SS(%d): '%s'", s->id, (char*)(s->smart_socket_data.data() + 4));
#if ADB_HOST
- service = &s->smart_socket_data[4];
- if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
- char* serial_end;
- service += strlen("host-serial:");
-
+ service = std::string_view(s->smart_socket_data).substr(4);
+ if (android::base::ConsumePrefix(&service, "host-serial:")) {
// serial number should follow "host:" and could be a host:port string.
- serial_end = internal::skip_host_serial(service);
- if (serial_end) {
- *serial_end = 0; // terminate string
- serial = service;
- service = serial_end + 1;
+ if (!internal::parse_host_service(&serial, &service, service)) {
+ LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
+ goto fail;
}
- } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
- service += strlen("host-transport-id:");
- transport_id = strtoll(service, &service, 10);
-
- if (*service != ':') {
+ } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
+ if (!ParseUint(&transport_id, service, &service)) {
+ LOG(ERROR) << "SS(" << s->id << "): failed to parse host transport id: " << service;
return -1;
}
- service++;
- } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
+ if (!android::base::ConsumePrefix(&service, ":")) {
+ LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
+ return -1;
+ }
+ } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
type = kTransportUsb;
- service += strlen("host-usb:");
- } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
+ } else if (android::base::ConsumePrefix(&service, "host-local:")) {
type = kTransportLocal;
- service += strlen("host-local:");
- } else if (!strncmp(service, "host:", strlen("host:"))) {
+ } else if (android::base::ConsumePrefix(&service, "host:")) {
type = kTransportAny;
- service += strlen("host:");
} else {
- service = nullptr;
+ service = std::string_view{};
}
- if (service) {
+ if (!service.empty()) {
asocket* s2;
// Some requests are handled immediately -- in that case the handle_host_request() routine
// has sent the OKAY or FAIL message and all we have to do is clean up.
- if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s)) {
- D("SS(%d): handled host service '%s'", s->id, service);
- goto fail;
- }
- if (!strncmp(service, "transport", strlen("transport"))) {
- D("SS(%d): okay transport", s->id);
- s->smart_socket_data.clear();
- return 0;
+ auto host_request_result = handle_host_request(
+ service, type, serial.empty() ? nullptr : std::string(serial).c_str(), transport_id,
+ s->peer->fd, s);
+
+ switch (host_request_result) {
+ case HostRequestResult::Handled:
+ LOG(VERBOSE) << "SS(" << s->id << "): handled host service '" << service << "'";
+ goto fail;
+
+ case HostRequestResult::SwitchedTransport:
+ D("SS(%d): okay transport", s->id);
+ s->smart_socket_data.clear();
+ return 0;
+
+ case HostRequestResult::Unhandled:
+ break;
}
/* try to find a local service with this name.
** if no such service exists, we'll fail out
** and tear down here.
*/
- s2 = create_host_service_socket(service, serial, transport_id);
+ // TODO: Convert to string_view.
+ s2 = host_service_to_socket(service, serial, transport_id);
if (s2 == nullptr) {
- D("SS(%d): couldn't create host service '%s'", s->id, service);
+ LOG(VERBOSE) << "SS(" << s->id << "): couldn't create host service '" << service << "'";
SendFail(s->peer->fd, "unknown host service");
goto fail;
}
@@ -762,7 +873,7 @@
/* give him our transport and upref it */
s->peer->transport = s->transport;
- connect_to_remote(s->peer, s->smart_socket_data.data() + 4);
+ connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
s->peer = nullptr;
s->close(s);
return 1;
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b8d7e06..b0e7fa0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -27,6 +27,7 @@
#include <errno.h>
#include <string>
+#include <string_view>
#include <vector>
// Include this before open/close/unlink are defined as macros below.
@@ -35,6 +36,7 @@
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
+#include "adb_unique_fd.h"
#include "sysdeps/errno.h"
#include "sysdeps/network.h"
#include "sysdeps/stat.h"
@@ -62,8 +64,6 @@
#include <memory> // unique_ptr
#include <string>
-#include "fdevent.h"
-
#define OS_PATH_SEPARATORS "\\/"
#define OS_PATH_SEPARATOR '\\'
#define OS_PATH_SEPARATOR_STR "\\"
@@ -75,42 +75,40 @@
extern int adb_thread_setname(const std::string& name);
-static __inline__ void close_on_exec(int fd)
-{
+static __inline__ void close_on_exec(borrowed_fd fd) {
/* nothing really */
}
-extern int adb_unlink(const char* path);
-#undef unlink
-#define unlink ___xxx_unlink
+extern int adb_unlink(const char* path);
+#undef unlink
+#define unlink ___xxx_unlink
extern int adb_mkdir(const std::string& path, int mode);
-#undef mkdir
-#define mkdir ___xxx_mkdir
+#undef mkdir
+#define mkdir ___xxx_mkdir
// See the comments for the !defined(_WIN32) versions of adb_*().
extern int adb_open(const char* path, int options);
extern int adb_creat(const char* path, int mode);
-extern int adb_read(int fd, void* buf, int len);
-extern int adb_write(int fd, const void* buf, int len);
-extern int64_t adb_lseek(int fd, int64_t pos, int where);
-extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
+extern int adb_read(borrowed_fd fd, void* buf, int len);
+extern int adb_write(borrowed_fd fd, const void* buf, int len);
+extern int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where);
+extern int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR);
extern int adb_close(int fd);
extern int adb_register_socket(SOCKET s);
// See the comments for the !defined(_WIN32) version of unix_close().
-static __inline__ int unix_close(int fd)
-{
+static __inline__ int unix_close(int fd) {
return close(fd);
}
-#undef close
-#define close ____xxx_close
+#undef close
+#define close ____xxx_close
// Like unix_read(), but may return EINTR.
-extern int unix_read_interruptible(int fd, void* buf, size_t len);
+extern int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len);
// See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(int fd, void* buf, size_t len) {
+static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
}
@@ -118,28 +116,26 @@
#define read ___xxx_read
// See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__ int unix_write(int fd, const void* buf, size_t len)
-{
- return write(fd, buf, len);
+static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
+ return write(fd.get(), buf, len);
}
#undef write
#define write ___xxx_write
// See the comments for the !defined(_WIN32) version of unix_lseek().
-static __inline__ int unix_lseek(int fd, int pos, int where) {
- return lseek(fd, pos, where);
+static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
+ return lseek(fd.get(), pos, where);
}
#undef lseek
#define lseek ___xxx_lseek
// See the comments for the !defined(_WIN32) version of adb_open_mode().
-static __inline__ int adb_open_mode(const char* path, int options, int mode)
-{
+static __inline__ int adb_open_mode(const char* path, int options, int mode) {
return adb_open(path, options);
}
// See the comments for the !defined(_WIN32) version of unix_open().
-extern int unix_open(const char* path, int options, ...);
+extern int unix_open(std::string_view path, int options, ...);
#define open ___xxx_unix_open
// Checks if |fd| corresponds to a console.
@@ -151,7 +147,7 @@
// with |fd| must have GENERIC_READ access (which console FDs have by default).
// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
// calling this function is unreliable and should not be used.
-int unix_isatty(int fd);
+int unix_isatty(borrowed_fd fd);
#define isatty ___xxx_isatty
int network_inaddr_any_server(int port, int type, std::string* error);
@@ -167,20 +163,21 @@
int network_connect(const std::string& host, int port, int type, int timeout,
std::string* error);
-extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
+extern int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen);
#undef accept
#define accept ___xxx_accept
// Returns the local port number of a bound socket, or -1 on failure.
-int adb_socket_get_local_port(int fd);
+int adb_socket_get_local_port(borrowed_fd fd);
-extern int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
+extern int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen);
#undef setsockopt
#define setsockopt ___xxx_setsockopt
-extern int adb_socketpair( int sv[2] );
+extern int adb_socketpair(int sv[2]);
struct adb_pollfd {
int fd;
@@ -213,8 +210,7 @@
extern int adb_fputc(int ch, FILE* stream);
extern int adb_putchar(int ch);
extern int adb_puts(const char* buf);
-extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
- FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);
extern FILE* adb_fopen(const char* f, const char* m);
@@ -343,9 +339,8 @@
return c == '/';
}
-static __inline__ void close_on_exec(int fd)
-{
- fcntl( fd, F_SETFD, FD_CLOEXEC );
+static __inline__ void close_on_exec(borrowed_fd fd) {
+ fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
}
// Open a file and return a file descriptor that may be used with unix_read(),
@@ -357,31 +352,26 @@
// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
// configurable CR/LF translation which defaults to text mode, but is settable
// with _setmode().
-static __inline__ int unix_open(const char* path, int options,...)
-{
- if ((options & O_CREAT) == 0)
- {
- return TEMP_FAILURE_RETRY( open(path, options) );
- }
- else
- {
- int mode;
- va_list args;
- va_start( args, options );
- mode = va_arg( args, int );
- va_end( args );
- return TEMP_FAILURE_RETRY( open( path, options, mode ) );
+static __inline__ int unix_open(std::string_view path, int options, ...) {
+ std::string zero_terminated(path.begin(), path.end());
+ if ((options & O_CREAT) == 0) {
+ return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options));
+ } else {
+ int mode;
+ va_list args;
+ va_start(args, options);
+ mode = va_arg(args, int);
+ va_end(args);
+ return TEMP_FAILURE_RETRY(open(zero_terminated.c_str(), options, mode));
}
}
// Similar to the two-argument adb_open(), but takes a mode parameter for file
// creation. See adb_open() for more info.
-static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
-{
- return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
+static __inline__ int adb_open_mode(const char* pathname, int options, int mode) {
+ return TEMP_FAILURE_RETRY(open(pathname, options, mode));
}
-
// Open a file and return a file descriptor that may be used with adb_read(),
// adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
//
@@ -389,23 +379,21 @@
// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
// and its CR/LF translation. The returned file descriptor should be used with
// adb_read(), adb_write(), adb_close(), etc.
-static __inline__ int adb_open( const char* pathname, int options )
-{
- int fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
- if (fd < 0)
- return -1;
- close_on_exec( fd );
+static __inline__ int adb_open(const char* pathname, int options) {
+ int fd = TEMP_FAILURE_RETRY(open(pathname, options));
+ if (fd < 0) return -1;
+ close_on_exec(fd);
return fd;
}
-#undef open
-#define open ___xxx_open
+#undef open
+#define open ___xxx_open
-static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
- return shutdown(fd, direction);
+static __inline__ int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR) {
+ return shutdown(fd.get(), direction);
}
-#undef shutdown
-#define shutdown ____xxx_shutdown
+#undef shutdown
+#define shutdown ____xxx_shutdown
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
@@ -413,81 +401,76 @@
__inline__ int adb_close(int fd) {
return close(fd);
}
-#undef close
-#define close ____xxx_close
+#undef close
+#define close ____xxx_close
// On Windows, ADB has an indirection layer for file descriptors. If we get a
// Win32 SOCKET object from an external library, we have to map it in to that
// indirection layer, which this does.
-__inline__ int adb_register_socket(int s) {
+__inline__ int adb_register_socket(int s) {
return s;
}
-static __inline__ int adb_read(int fd, void* buf, size_t len)
-{
- return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
+static __inline__ int adb_read(borrowed_fd fd, void* buf, size_t len) {
+ return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
}
// Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
- return read(fd, buf, len);
+static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+ return read(fd.get(), buf, len);
}
-#undef read
-#define read ___xxx_read
+#undef read
+#define read ___xxx_read
-static __inline__ int adb_write(int fd, const void* buf, size_t len)
-{
- return TEMP_FAILURE_RETRY( write( fd, buf, len ) );
+static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
+ return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
}
#undef write
#define write ___xxx_write
-static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
+static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
#if defined(__APPLE__)
- return lseek(fd, pos, where);
+ return lseek(fd.get(), pos, where);
#else
- return lseek64(fd, pos, where);
+ return lseek64(fd.get(), pos, where);
#endif
}
#undef lseek
#define lseek ___xxx_lseek
-static __inline__ int adb_unlink(const char* path)
-{
- return unlink(path);
+static __inline__ int adb_unlink(const char* path) {
+ return unlink(path);
}
-#undef unlink
-#define unlink ___xxx_unlink
+#undef unlink
+#define unlink ___xxx_unlink
-static __inline__ int adb_creat(const char* path, int mode)
-{
- int fd = TEMP_FAILURE_RETRY( creat( path, mode ) );
+static __inline__ int adb_creat(const char* path, int mode) {
+ int fd = TEMP_FAILURE_RETRY(creat(path, mode));
- if ( fd < 0 )
- return -1;
+ if (fd < 0) return -1;
close_on_exec(fd);
return fd;
}
-#undef creat
-#define creat ___xxx_creat
+#undef creat
+#define creat ___xxx_creat
-static __inline__ int unix_isatty(int fd) {
- return isatty(fd);
+static __inline__ int unix_isatty(borrowed_fd fd) {
+ return isatty(fd.get());
}
-#define isatty ___xxx_isatty
+#define isatty ___xxx_isatty
// Helper for network_* functions.
inline int _fd_set_error_str(int fd, std::string* error) {
- if (fd == -1) {
- *error = strerror(errno);
- }
- return fd;
+ if (fd == -1) {
+ *error = strerror(errno);
+ }
+ return fd;
}
inline int network_inaddr_any_server(int port, int type, std::string* error) {
- return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+ return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
inline int network_local_client(const char* name, int namespace_id, int type, std::string* error) {
@@ -500,22 +483,21 @@
int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
-static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
-{
+static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
+ socklen_t* addrlen) {
int fd;
- fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
- if (fd >= 0)
- close_on_exec(fd);
+ fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
+ if (fd >= 0) close_on_exec(fd);
return fd;
}
-#undef accept
-#define accept ___xxx_accept
+#undef accept
+#define accept ___xxx_accept
-inline int adb_socket_get_local_port(int fd) {
- return socket_get_local_port(fd);
+inline int adb_socket_get_local_port(borrowed_fd fd) {
+ return socket_get_local_port(fd.get());
}
// Operate on a file descriptor returned from unix_open() or a well-known file
@@ -526,10 +508,10 @@
// Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
// into the C Runtime and its configurable CR/LF translation (which is settable
// via _setmode()).
-#define unix_read adb_read
-#define unix_write adb_write
+#define unix_read adb_read
+#define unix_write adb_write
#define unix_lseek adb_lseek
-#define unix_close adb_close
+#define unix_close adb_close
static __inline__ int adb_thread_setname(const std::string& name) {
#ifdef __APPLE__
@@ -544,34 +526,31 @@
#endif
}
-static __inline__ int adb_setsockopt( int fd, int level, int optname, const void* optval, socklen_t optlen )
-{
- return setsockopt( fd, level, optname, optval, optlen );
+static __inline__ int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
+ socklen_t optlen) {
+ return setsockopt(fd.get(), level, optname, optval, optlen);
}
-#undef setsockopt
-#define setsockopt ___xxx_setsockopt
+#undef setsockopt
+#define setsockopt ___xxx_setsockopt
-static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
-{
- return socketpair( d, type, protocol, sv );
+static __inline__ int unix_socketpair(int d, int type, int protocol, int sv[2]) {
+ return socketpair(d, type, protocol, sv);
}
-static __inline__ int adb_socketpair( int sv[2] )
-{
- int rc;
+static __inline__ int adb_socketpair(int sv[2]) {
+ int rc;
- rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
- if (rc < 0)
- return -1;
+ rc = unix_socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ if (rc < 0) return -1;
- close_on_exec( sv[0] );
- close_on_exec( sv[1] );
+ close_on_exec(sv[0]);
+ close_on_exec(sv[1]);
return 0;
}
-#undef socketpair
-#define socketpair ___xxx_socketpair
+#undef socketpair
+#define socketpair ___xxx_socketpair
typedef struct pollfd adb_pollfd;
static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
@@ -580,13 +559,12 @@
#define poll ___xxx_poll
-static __inline__ int adb_mkdir(const std::string& path, int mode)
-{
+static __inline__ int adb_mkdir(const std::string& path, int mode) {
return mkdir(path.c_str(), mode);
}
-#undef mkdir
-#define mkdir ___xxx_mkdir
+#undef mkdir
+#define mkdir ___xxx_mkdir
static __inline__ int adb_is_absolute_host_path(const char* path) {
return path[0] == '/';
@@ -594,15 +572,15 @@
#endif /* !_WIN32 */
-static inline void disable_tcp_nagle(int fd) {
+static inline void disable_tcp_nagle(borrowed_fd fd) {
int off = 1;
- adb_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+ adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
}
// Sets TCP socket |fd| to send a keepalive TCP message every |interval_sec| seconds. Set
// |interval_sec| to 0 to disable keepalives. If keepalives are enabled, the connection will be
// configured to drop after 10 missed keepalives. Returns true on success.
-bool set_tcp_keepalive(int fd, int interval_sec);
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec);
#if defined(_WIN32)
// Win32 defines ERROR, which we don't need, but which conflicts with google3 logging.
diff --git a/adb/sysdeps/chrono.h b/adb/sysdeps/chrono.h
index c73a638..5c5af7c 100644
--- a/adb/sysdeps/chrono.h
+++ b/adb/sysdeps/chrono.h
@@ -18,29 +18,4 @@
#include <chrono>
-#if defined(_WIN32)
-// We don't have C++14 on Windows yet.
-// Reimplement std::chrono_literals ourselves until we do.
-
-// Silence the following warning (which gets promoted to an error):
-// error: literal operator suffixes not preceded by ‘_’ are reserved for future standardization
-#pragma GCC system_header
-
-constexpr std::chrono::seconds operator"" s(unsigned long long s) {
- return std::chrono::seconds(s);
-}
-
-constexpr std::chrono::duration<long double> operator"" s(long double s) {
- return std::chrono::duration<long double>(s);
-}
-
-constexpr std::chrono::milliseconds operator"" ms(unsigned long long ms) {
- return std::chrono::milliseconds(ms);
-}
-
-constexpr std::chrono::duration<long double, std::milli> operator"" ms(long double ms) {
- return std::chrono::duration<long double, std::milli>(ms);
-}
-#else
using namespace std::chrono_literals;
-#endif
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index 33ddb4e..c5c2275 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -24,6 +24,7 @@
#include <string>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include "adb_unique_fd.h"
@@ -103,13 +104,13 @@
socklen_t addrlen = sizeof(addr_storage);
sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
- if (bind(s, addr, addrlen) != 0) {
+ if (bind(s.get(), addr, addrlen) != 0) {
set_error(error);
return -1;
}
if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
- if (listen(s, SOMAXCONN) != 0) {
+ if (listen(s.get(), SOMAXCONN) != 0) {
set_error(error);
return -1;
}
@@ -136,11 +137,13 @@
return fd;
}
if (getaddrinfo_error != 0) {
- *error = gai_strerror(getaddrinfo_error);
- LOG(WARNING) << "failed to resolve host '" << host << "': " << *error;
+ *error = android::base::StringPrintf("failed to resolve host: '%s': %s", host.c_str(),
+ gai_strerror(getaddrinfo_error));
+ LOG(WARNING) << *error;
} else {
- *error = strerror(errno);
- LOG(WARNING) << "failed to connect to '" << host << "': " << *error;
+ *error = android::base::StringPrintf("failed to connect to '%s:%d': %s", host.c_str(), port,
+ strerror(errno));
+ LOG(WARNING) << *error;
}
return -1;
}
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
index 2c2e0ee..67155d9 100644
--- a/adb/sysdeps/stat_test.cpp
+++ b/adb/sysdeps/stat_test.cpp
@@ -16,7 +16,7 @@
#include <string>
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include "adb_utils.h"
diff --git a/adb/sysdeps/uio.h b/adb/sysdeps/uio.h
index d06ef89..ced884b 100644
--- a/adb/sysdeps/uio.h
+++ b/adb/sysdeps/uio.h
@@ -18,6 +18,8 @@
#include <sys/types.h>
+#include "adb_unique_fd.h"
+
#if defined(_WIN32)
// Layout of this struct must match struct WSABUF (verified via static assert in sysdeps_win32.cpp)
@@ -26,13 +28,15 @@
void* iov_base;
};
-ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt);
+ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt);
#else
#include <sys/uio.h>
using adb_iovec = struct iovec;
-#define adb_writev writev
+inline ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
+ return writev(fd.get(), iov, iovcnt);
+}
#endif
diff --git a/adb/sysdeps/vm_sockets.h b/adb/sysdeps/vm_sockets.h
new file mode 100644
index 0000000..75c5f44
--- /dev/null
+++ b/adb/sysdeps/vm_sockets.h
@@ -0,0 +1,49 @@
+#if __BIONIC__
+#include <linux/vm_sockets.h>
+#else
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#include <linux/socket.h>
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY -1U
+#define VMADDR_PORT_ANY -1U
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_RESERVED 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION -1U
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF))
+struct sockaddr_vm {
+ __kernel_sa_family_t svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) -
+ sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
+#endif
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 79cebe6..0f4b39c 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,6 +25,21 @@
#include "sysdeps.h"
#include "sysdeps/chrono.h"
+#if defined(_WIN32)
+#include <windows.h>
+static bool IsWine() {
+ HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
+ if (!ntdll) {
+ return false;
+ }
+ return GetProcAddress(ntdll, "wine_get_version") != nullptr;
+}
+#else
+static bool IsWine() {
+ return false;
+}
+#endif
+
TEST(sysdeps_socketpair, smoke) {
int fds[2];
ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -182,8 +197,10 @@
EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
- // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
- EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+ if (!IsWine()) {
+ // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+ EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+ }
}
TEST_F(sysdeps_poll, fd_count) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 4445a44..3fdc917 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -16,7 +16,7 @@
#include "sysdeps.h"
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
int enable = (interval_sec > 0);
if (adb_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable))) {
return false;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 8a6541d..6372b3d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
#include <memory>
#include <mutex>
#include <string>
+#include <string_view>
#include <unordered_map>
#include <vector>
@@ -144,16 +145,14 @@
static FHRec _win32_fhs[ WIN32_MAX_FHS ];
static int _win32_fh_next; // where to start search for free FHRec
-static FH
-_fh_from_int( int fd, const char* func )
-{
- FH f;
+static FH _fh_from_int(borrowed_fd bfd, const char* func) {
+ FH f;
+ int fd = bfd.get();
fd -= WIN32_FH_BASE;
if (fd < 0 || fd >= WIN32_MAX_FHS) {
- D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
- func );
+ D("_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE, func);
errno = EBADF;
return nullptr;
}
@@ -161,8 +160,7 @@
f = &_win32_fhs[fd];
if (f->used == 0) {
- D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
- func );
+ D("_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE, func);
errno = EBADF;
return nullptr;
}
@@ -170,20 +168,15 @@
return f;
}
-
-static int
-_fh_to_int( FH f )
-{
+static int _fh_to_int(FH f) {
if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
return (int)(f - _win32_fhs) + WIN32_FH_BASE;
return -1;
}
-static FH
-_fh_alloc( FHClass clazz )
-{
- FH f = nullptr;
+static FH _fh_alloc(FHClass clazz) {
+ FH f = nullptr;
std::lock_guard<std::mutex> lock(_win32_lock);
@@ -205,10 +198,7 @@
return nullptr;
}
-
-static int
-_fh_close( FH f )
-{
+static int _fh_close(FH f) {
// Use lock so that closing only happens once and so that _fh_alloc can't
// allocate a FH that we're in the middle of closing.
std::lock_guard<std::mutex> lock(_win32_lock);
@@ -355,6 +345,9 @@
DWORD desiredAccess = 0;
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ // CreateFileW is inherently O_CLOEXEC by default.
+ options &= ~O_CLOEXEC;
+
switch (options) {
case O_RDONLY:
desiredAccess = GENERIC_READ;
@@ -452,7 +445,7 @@
return _fh_to_int(f);
}
-int adb_read(int fd, void* buf, int len) {
+int adb_read(borrowed_fd fd, void* buf, int len) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -463,7 +456,7 @@
return f->clazz->_fh_read(f, buf, len);
}
-int adb_write(int fd, const void* buf, int len) {
+int adb_write(borrowed_fd fd, const void* buf, int len) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -474,7 +467,7 @@
return f->clazz->_fh_write(f, buf, len);
}
-ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
+ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
FH f = _fh_from_int(fd, __func__);
if (f == nullptr) {
@@ -485,7 +478,7 @@
return f->clazz->_fh_writev(f, iov, iovcnt);
}
-int64_t adb_lseek(int fd, int64_t pos, int where) {
+int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
FH f = _fh_from_int(fd, __func__);
if (!f) {
errno = EBADF;
@@ -617,15 +610,6 @@
static int _fh_socket_close(FH f) {
if (f->fh_socket != INVALID_SOCKET) {
- /* gently tell any peer that we're closing the socket */
- if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
- // If the socket is not connected, this returns an error. We want to
- // minimize logging spam, so don't log these errors for now.
-#if 0
- D("socket shutdown failed: %s",
- android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-#endif
- }
if (closesocket(f->fh_socket) == SOCKET_ERROR) {
// Don't set errno here, since adb_close will ignore it.
const DWORD err = WSAGetLastError();
@@ -718,7 +702,7 @@
/**************************************************************************/
/**************************************************************************/
-static int _init_winsock(void) {
+static void _init_winsock() {
static std::once_flag once;
std::call_once(once, []() {
WSADATA wsaData;
@@ -742,11 +726,8 @@
// crypt32.dll which calls atexit() which tries to acquire the C
// Runtime lock that the other thread holds.
});
- return 0;
}
-static int _winsock_init = _init_winsock();
-
// Map a socket type to an explicit socket protocol instead of using the socket
// protocol of 0. Explicit socket protocols are used by most apps and we should
// do the same to reduce the chance of exercising uncommon code-paths that might
@@ -972,11 +953,11 @@
}
#undef accept
-int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t* addrlen) {
+int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
FH serverfh = _fh_from_int(serverfd, __func__);
if (!serverfh || serverfh->clazz != &_fh_socket_class) {
- D("adb_socket_accept: invalid fd %d", serverfd);
+ D("adb_socket_accept: invalid fd %d", serverfd.get());
errno = EBADF;
return -1;
}
@@ -991,7 +972,7 @@
fh->fh_socket = accept(serverfh->fh_socket, addr, addrlen);
if (fh->fh_socket == INVALID_SOCKET) {
const DWORD err = WSAGetLastError();
- LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd
+ LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd.get()
<< " failed: " + android::base::SystemErrorCodeToString(err);
_socket_set_errno(err);
return -1;
@@ -999,16 +980,16 @@
const int fd = _fh_to_int(fh.get());
snprintf(fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name);
- D("adb_socket_accept on fd %d returns fd %d", serverfd, fd);
+ D("adb_socket_accept on fd %d returns fd %d", serverfd.get(), fd);
fh.release();
return fd;
}
-int adb_setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
+int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval, socklen_t optlen) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("adb_setsockopt: invalid fd %d", fd);
+ D("adb_setsockopt: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1021,7 +1002,7 @@
setsockopt(fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd, level,
+ D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd.get(), level,
optname, android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
result = -1;
@@ -1029,11 +1010,11 @@
return result;
}
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+static int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("adb_getsockname: invalid fd %d", fd);
+ D("adb_getsockname: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1041,7 +1022,7 @@
int result = getsockname(fh->fh_socket, sockaddr, optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+ D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
result = -1;
@@ -1049,7 +1030,7 @@
return result;
}
-int adb_socket_get_local_port(int fd) {
+int adb_socket_get_local_port(borrowed_fd fd) {
sockaddr_storage addr_storage;
socklen_t addr_len = sizeof(addr_storage);
@@ -1067,11 +1048,11 @@
return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
}
-int adb_shutdown(int fd, int direction) {
+int adb_shutdown(borrowed_fd fd, int direction) {
FH f = _fh_from_int(fd, __func__);
if (!f || f->clazz != &_fh_socket_class) {
- D("adb_shutdown: invalid fd %d", fd);
+ D("adb_shutdown: invalid fd %d", fd.get());
errno = EBADF;
return -1;
}
@@ -1079,7 +1060,7 @@
D("adb_shutdown: %s", f->name);
if (shutdown(f->fh_socket, direction) == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
- D("socket shutdown fd %d failed: %s", fd,
+ D("socket shutdown fd %d failed: %s", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
return -1;
@@ -1137,12 +1118,12 @@
return -1;
}
-bool set_file_block_mode(int fd, bool block) {
+bool set_file_block_mode(borrowed_fd fd, bool block) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || !fh->used) {
errno = EBADF;
- D("Setting nonblocking on bad file descriptor %d", fd);
+ D("Setting nonblocking on bad file descriptor %d", fd.get());
return false;
}
@@ -1151,22 +1132,22 @@
if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
int error = WSAGetLastError();
_socket_set_errno(error);
- D("Setting %d nonblocking failed (%d)", fd, error);
+ D("Setting %d nonblocking failed (%d)", fd.get(), error);
return false;
}
return true;
} else {
errno = ENOTSOCK;
- D("Setting nonblocking on non-socket %d", fd);
+ D("Setting nonblocking on non-socket %d", fd.get());
return false;
}
}
-bool set_tcp_keepalive(int fd, int interval_sec) {
+bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
FH fh = _fh_from_int(fd, __func__);
if (!fh || fh->clazz != &_fh_socket_class) {
- D("set_tcp_keepalive(%d) failed: invalid fd", fd);
+ D("set_tcp_keepalive(%d) failed: invalid fd", fd.get());
errno = EBADF;
return false;
}
@@ -1180,7 +1161,7 @@
if (WSAIoctl(fh->fh_socket, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), nullptr, 0,
&bytes_returned, nullptr, nullptr) != 0) {
const DWORD err = WSAGetLastError();
- D("set_tcp_keepalive(%d) failed: %s", fd,
+ D("set_tcp_keepalive(%d) failed: %s", fd.get(),
android::base::SystemErrorCodeToString(err).c_str());
_socket_set_errno(err);
return false;
@@ -1227,12 +1208,12 @@
// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
-static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+static HANDLE _get_console_handle(borrowed_fd fd, DWORD* mode = nullptr) {
// First check isatty(); this is very fast and eliminates most non-console
// FDs, but returns 1 for both consoles and character devices like NUL.
#pragma push_macro("isatty")
#undef isatty
- if (!isatty(fd)) {
+ if (!isatty(fd.get())) {
return nullptr;
}
#pragma pop_macro("isatty")
@@ -1240,7 +1221,7 @@
// To differentiate between character devices and consoles we need to get
// the underlying HANDLE and use GetConsoleMode(), which is what requires
// GENERIC_READ permissions.
- const intptr_t intptr_handle = _get_osfhandle(fd);
+ const intptr_t intptr_handle = _get_osfhandle(fd.get());
if (intptr_handle == -1) {
return nullptr;
}
@@ -1264,7 +1245,7 @@
return _get_console_handle(fd);
}
-int unix_isatty(int fd) {
+int unix_isatty(borrowed_fd fd) {
return _get_console_handle(fd) ? 1 : 0;
}
@@ -1644,7 +1625,7 @@
// Prefix the len bytes in buf with the escape character, and then return the
// new buffer length.
-size_t _escape_prefix(char* const buf, const size_t len) {
+static size_t _escape_prefix(char* const buf, const size_t len) {
// If nothing to prefix, don't do anything. We might be called with
// len == 0, if alt was held down with a dead key which produced nothing.
if (len == 0) {
@@ -2072,7 +2053,7 @@
}
// Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
-int unix_read_interruptible(int fd, void* buf, size_t len) {
+int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
if ((fd == STDIN_FILENO) && (_console_handle != nullptr)) {
// If it is a request to read from stdin, and stdin_raw_init() has been
// called, and it successfully configured the console, then read from
@@ -2092,7 +2073,7 @@
// plain read() in favor of unix_read() or adb_read().
#pragma push_macro("read")
#undef read
- return read(fd, buf, len);
+ return read(fd.get(), buf, len);
#pragma pop_macro("read")
}
}
@@ -2203,15 +2184,15 @@
}
}
-int unix_open(const char* path, int options, ...) {
+int unix_open(std::string_view path, int options, ...) {
std::wstring path_wide;
- if (!android::base::UTF8ToWide(path, &path_wide)) {
+ if (!android::base::UTF8ToWide(path.data(), path.size(), &path_wide)) {
return -1;
}
if ((options & O_CREAT) == 0) {
return _wopen(path_wide.c_str(), options);
} else {
- int mode;
+ int mode;
va_list args;
va_start(args, options);
mode = va_arg(args, int);
@@ -2616,18 +2597,19 @@
extern "C" int wmain(int argc, wchar_t **argv) {
// Convert args from UTF-16 to UTF-8 and pass that to main().
NarrowArgs narrow_args(argc, argv);
- return main(argc, narrow_args.data());
+
+ // Avoid destructing NarrowArgs: argv might have been mutated to point to string literals.
+ _exit(main(argc, narrow_args.data()));
}
// Shadow UTF-8 environment variable name/value pairs that are created from
-// _wenviron the first time that adb_getenv() is called. Note that this is not
-// currently updated if putenv, setenv, unsetenv are called. Note that no
-// thread synchronization is done, but we're called early enough in
+// _wenviron by _init_env(). Note that this is not currently updated if putenv, setenv, unsetenv are
+// called. Note that no thread synchronization is done, but we're called early enough in
// single-threaded startup that things work ok.
static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
-// Make sure that shadow UTF-8 environment variables are setup.
-static void _ensure_env_setup() {
+// Setup shadow UTF-8 environment variables.
+static void _init_env() {
// If some name/value pairs exist, then we've already done the setup below.
if (g_environ_utf8.size() != 0) {
return;
@@ -2680,8 +2662,6 @@
// Version of getenv() that takes a UTF-8 environment variable name and
// retrieves a UTF-8 value. Case-insensitive to match getenv() on Windows.
char* adb_getenv(const char* name) {
- _ensure_env_setup();
-
// Case-insensitive search by searching for lowercase name in a map of
// lowercase names.
const auto it = g_environ_utf8.find(ToLower(std::string(name)));
@@ -2756,3 +2736,65 @@
return 0;
}
+
+#if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#if !defined(DISABLE_NEWLINE_AUTO_RETURN)
+#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
+#endif
+
+static void _init_console() {
+ DWORD old_out_console_mode;
+
+ const HANDLE out = _get_console_handle(STDOUT_FILENO, &old_out_console_mode);
+ if (out == nullptr) {
+ return;
+ }
+
+ // Try to use ENABLE_VIRTUAL_TERMINAL_PROCESSING on the output console to process virtual
+ // terminal sequences on newer versions of Windows 10 and later.
+ // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
+ // On older OSes that don't support the flag, SetConsoleMode() will return an error.
+ // ENABLE_VIRTUAL_TERMINAL_PROCESSING also solves a problem where the last column of the
+ // console cannot be overwritten.
+ //
+ // Note that we don't use DISABLE_NEWLINE_AUTO_RETURN because it doesn't seem to be necessary.
+ // If we use DISABLE_NEWLINE_AUTO_RETURN, _console_write_utf8() would need to be modified to
+ // translate \n to \r\n.
+ if (!SetConsoleMode(out, old_out_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
+ return;
+ }
+
+ // If SetConsoleMode() succeeded, the console supports virtual terminal processing, so we
+ // should set the TERM env var to match so that it will be propagated to adbd on devices.
+ //
+ // Below's direct manipulation of env vars and not g_environ_utf8 assumes that _init_env() has
+ // not yet been called. If this fails, _init_env() should be called after _init_console().
+ if (g_environ_utf8.size() > 0) {
+ LOG(FATAL) << "environment variables have already been converted to UTF-8";
+ }
+
+#pragma push_macro("getenv")
+#undef getenv
+#pragma push_macro("putenv")
+#undef putenv
+ if (getenv("TERM") == nullptr) {
+ // This is the same TERM value used by Gnome Terminal and the version of ssh included with
+ // Windows.
+ putenv("TERM=xterm-256color");
+ }
+#pragma pop_macro("putenv")
+#pragma pop_macro("getenv")
+}
+
+static bool _init_sysdeps() {
+ // _init_console() depends on _init_env() not being called yet.
+ _init_console();
+ _init_env();
+ _init_winsock();
+ return true;
+}
+
+static bool _sysdeps_init = _init_sysdeps();
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 529b212..183cd5b 100644
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,7 +18,7 @@
#include "sysdeps.h"
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
TEST(sysdeps_win32, adb_getenv) {
// Insert all test env vars before first call to adb_getenv() which will
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 430fc3d..8272722 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -416,12 +416,16 @@
output.strip(),
"already connected to {}".format(serial).encode("utf8"))
+ @unittest.skip("Currently failing b/123247844")
def test_reconnect(self):
"""Ensure that a disconnected device reconnects."""
with fake_adbd() as (port, _):
serial = "localhost:{}".format(port)
with adb_connect(self, serial):
+ # Wait a bit to give adb some time to connect.
+ time.sleep(0.25)
+
output = subprocess.check_output(["adb", "-s", serial,
"get-state"])
self.assertEqual(output.strip(), b"device")
diff --git a/adb/test_device.py b/adb/test_device.py
index 34f8fd9..f95a5b3 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -35,6 +35,8 @@
import time
import unittest
+from datetime import datetime
+
import adb
def requires_root(func):
@@ -1335,6 +1337,63 @@
self.device.forward_remove("tcp:{}".format(local_port))
+class SocketTest(DeviceTest):
+ def test_socket_flush(self):
+ """Test that we handle socket closure properly.
+
+ If we're done writing to a socket, closing before the other end has
+ closed will send a TCP_RST if we have incoming data queued up, which
+ may result in data that we've written being discarded.
+
+ Bug: http://b/74616284
+ """
+ s = socket.create_connection(("localhost", 5037))
+
+ def adb_length_prefixed(string):
+ encoded = string.encode("utf8")
+ result = b"%04x%s" % (len(encoded), encoded)
+ return result
+
+ if "ANDROID_SERIAL" in os.environ:
+ transport_string = "host:transport:" + os.environ["ANDROID_SERIAL"]
+ else:
+ transport_string = "host:transport-any"
+
+ s.sendall(adb_length_prefixed(transport_string))
+ response = s.recv(4)
+ self.assertEquals(b"OKAY", response)
+
+ shell_string = "shell:sleep 0.5; dd if=/dev/zero bs=1m count=1 status=none; echo foo"
+ s.sendall(adb_length_prefixed(shell_string))
+
+ response = s.recv(4)
+ self.assertEquals(b"OKAY", response)
+
+ # Spawn a thread that dumps garbage into the socket until failure.
+ def spam():
+ buf = b"\0" * 16384
+ try:
+ while True:
+ s.sendall(buf)
+ except Exception as ex:
+ print(ex)
+
+ thread = threading.Thread(target=spam)
+ thread.start()
+
+ time.sleep(1)
+
+ received = b""
+ while True:
+ read = s.recv(512)
+ if len(read) == 0:
+ break
+ received += read
+
+ self.assertEquals(1024 * 1024 + len("foo\n"), len(received))
+ thread.join()
+
+
if sys.platform == "win32":
# From https://stackoverflow.com/a/38749458
import os
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 03a9f30..3d1d620 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -49,9 +49,11 @@
#include "adb_io.h"
#include "adb_trace.h"
#include "adb_utils.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps/chrono.h"
+using android::base::ScopedLockAssertion;
+
static void remove_transport(atransport* transport);
static void transport_unref(atransport* transport);
@@ -68,20 +70,12 @@
const char* const kFeaturePushSync = "push_sync";
const char* const kFeatureApex = "apex";
const char* const kFeatureFixedPushMkdir = "fixed_push_mkdir";
+const char* const kFeatureAbb = "abb";
+const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
+const char* const kFeatureAbbExec = "abb_exec";
namespace {
-// A class that helps the Clang Thread Safety Analysis deal with
-// std::unique_lock. Given that std::unique_lock is movable, and the analysis
-// can not currently perform alias analysis, it is not annotated. In order to
-// assert that the mutex is held, a ScopedAssumeLocked can be created just after
-// the std::unique_lock.
-class SCOPED_CAPABILITY ScopedAssumeLocked {
- public:
- ScopedAssumeLocked(std::mutex& mutex) ACQUIRE(mutex) {}
- ~ScopedAssumeLocked() RELEASE() {}
-};
-
#if ADB_HOST
// Tracks and handles atransport*s that are attempting reconnection.
class ReconnectHandler {
@@ -179,7 +173,7 @@
ReconnectAttempt attempt;
{
std::unique_lock<std::mutex> lock(reconnect_mutex_);
- ScopedAssumeLocked assume_lock(reconnect_mutex_);
+ ScopedLockAssertion assume_lock(reconnect_mutex_);
if (!reconnect_queue_.empty()) {
// FIXME: libstdc++ (used on Windows) implements condition_variable with
@@ -263,6 +257,11 @@
return next++;
}
+void Connection::Reset() {
+ LOG(INFO) << "Connection::Reset(): stopping";
+ Stop();
+}
+
BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
: underlying_(std::move(connection)) {}
@@ -295,7 +294,7 @@
LOG(INFO) << this->transport_name_ << ": write thread spawning";
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
cv_.wait(lock, [this]() REQUIRES(mutex_) {
return this->stopped_ || !this->write_queue_.empty();
});
@@ -318,6 +317,26 @@
started_ = true;
}
+void BlockingConnectionAdapter::Reset() {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!started_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+ return;
+ }
+
+ if (stopped_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
+ << "): already stopped";
+ return;
+ }
+ }
+
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): resetting";
+ this->underlying_->Reset();
+ Stop();
+}
+
void BlockingConnectionAdapter::Stop() {
{
std::lock_guard<std::mutex> lock(mutex_);
@@ -430,14 +449,18 @@
}
}
-void kick_transport(atransport* t) {
+void kick_transport(atransport* t, bool reset) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
// As kick_transport() can be called from threads without guarantee that t is valid,
// check if the transport is in transport_list first.
//
// TODO(jmgao): WTF? Is this actually true?
if (std::find(transport_list.begin(), transport_list.end(), t) != transport_list.end()) {
- t->Kick();
+ if (reset) {
+ t->Reset();
+ } else {
+ t->Kick();
+ }
}
#if ADB_HOST
@@ -519,9 +542,7 @@
// for the first time, even if no update occurred.
if (tracker->update_needed) {
tracker->update_needed = false;
-
- std::string transports = list_transports(tracker->long_output);
- device_tracker_send(tracker, transports);
+ device_tracker_send(tracker, list_transports(tracker->long_output));
}
}
@@ -564,13 +585,11 @@
update_transport_status();
// Notify `adb track-devices` clients.
- std::string transports = list_transports(false);
-
device_tracker* tracker = device_tracker_list;
while (tracker != nullptr) {
device_tracker* next = tracker->next;
// This may destroy the tracker if the connection is closed.
- device_tracker_send(tracker, transports);
+ device_tracker_send(tracker, list_transports(tracker->long_output));
tracker = next;
}
}
@@ -764,6 +783,10 @@
#if ADB_HOST
if (t->IsTcpDevice() && !t->kicked()) {
D("transport: %s unref (attempting reconnection)", t->serial.c_str());
+
+ // We need to clear the transport's keys, so that on the next connection, it tries
+ // again from the beginning.
+ t->ResetKeys();
reconnect_handler.TrackTransport(t);
} else {
D("transport: %s unref (kicking and closing)", t->serial.c_str());
@@ -918,7 +941,7 @@
bool ConnectionWaitable::WaitForConnection(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mutex_);
- ScopedAssumeLocked assume_locked(mutex_);
+ ScopedLockAssertion assume_locked(mutex_);
return cv_.wait_for(lock, timeout, [&]() REQUIRES(mutex_) {
return connection_established_ready_;
}) && connection_established_;
@@ -944,9 +967,16 @@
return this->connection()->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
}
+void atransport::Reset() {
+ if (!kicked_.exchange(true)) {
+ LOG(INFO) << "resetting transport " << this << " " << this->serial;
+ this->connection()->Reset();
+ }
+}
+
void atransport::Kick() {
if (!kicked_.exchange(true)) {
- D("kicking transport %p %s", this, this->serial.c_str());
+ LOG(INFO) << "kicking transport " << this << " " << this->serial;
this->connection()->Stop();
}
}
@@ -978,6 +1008,8 @@
return "host";
case kCsRecovery:
return "recovery";
+ case kCsRescue:
+ return "rescue";
case kCsNoPerm:
return UsbNoPermissionsShortHelpText();
case kCsSideload:
@@ -1009,13 +1041,18 @@
const FeatureSet& supported_features() {
// Local static allocation to avoid global non-POD variables.
static const FeatureSet* features = new FeatureSet{
- kFeatureShell2, kFeatureCmd, kFeatureStat2, kFeatureFixedPushMkdir,
-#if ADB_HOST
- kFeatureApex
-#endif
- // Increment ADB_SERVER_VERSION whenever the feature list changes to
- // make sure that the adb client and server features stay in sync
- // (http://b/24370690).
+ kFeatureShell2,
+ kFeatureCmd,
+ kFeatureStat2,
+ kFeatureFixedPushMkdir,
+ kFeatureApex,
+ kFeatureAbb,
+ kFeatureFixedPushSymlinkTimestamp,
+ kFeatureAbbExec,
+ // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
+ // to know about. Otherwise, the client can be stuck running an old
+ // version of the server even after upgrading their copy of adb.
+ // (http://b/24370690)
};
return *features;
@@ -1170,18 +1207,22 @@
return result;
}
-void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto& t : transport_list) {
if (predicate(t)) {
- t->Kick();
+ if (reset) {
+ t->Reset();
+ } else {
+ t->Kick();
+ }
}
}
}
/* hack for osx */
-void close_usb_devices() {
- close_usb_devices([](const atransport*) { return true; });
+void close_usb_devices(bool reset) {
+ close_usb_devices([](const atransport*) { return true; }, reset);
}
#endif // ADB_HOST
@@ -1303,11 +1344,7 @@
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
transport_list.remove_if([usb](atransport* t) {
- auto connection = t->connection();
- if (auto usb_connection = dynamic_cast<UsbConnection*>(connection.get())) {
- return usb_connection->handle_ == usb && t->GetConnectionState() == kCsNoPerm;
- }
- return false;
+ return t->GetUsbHandle() == usb && t->GetConnectionState() == kCsNoPerm;
});
}
#endif
@@ -1330,10 +1367,20 @@
#if ADB_HOST
std::shared_ptr<RSA> atransport::NextKey() {
- if (keys_.empty()) keys_ = adb_auth_get_private_keys();
+ if (keys_.empty()) {
+ LOG(INFO) << "fetching keys for transport " << this->serial_name();
+ keys_ = adb_auth_get_private_keys();
+
+ // We should have gotten at least one key: the one that's automatically generated.
+ CHECK(!keys_.empty());
+ }
std::shared_ptr<RSA> result = keys_[0];
keys_.pop_front();
return result;
}
+
+void atransport::ResetKeys() {
+ keys_.clear();
+}
#endif
diff --git a/adb/transport.h b/adb/transport.h
index 9894bdf..f4490ed 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -28,6 +28,7 @@
#include <memory>
#include <mutex>
#include <string>
+#include <string_view>
#include <thread>
#include <unordered_set>
@@ -37,6 +38,7 @@
#include "adb.h"
#include "adb_unique_fd.h"
+#include "usb.h"
typedef std::unordered_set<std::string> FeatureSet;
@@ -63,6 +65,10 @@
extern const char* const kFeatureApex;
// adbd has b/110953234 fixed.
extern const char* const kFeatureFixedPushMkdir;
+// adbd supports android binder bridge (abb).
+extern const char* const kFeatureAbb;
+// adbd properly updates symlink timestamps on push.
+extern const char* const kFeatureFixedPushSymlinkTimestamp;
TransportId NextTransportId();
@@ -93,6 +99,9 @@
virtual void Start() = 0;
virtual void Stop() = 0;
+ // Stop, and reset the device if it's a USB connection.
+ virtual void Reset();
+
std::string transport_name_;
ReadCallback read_callback_;
ErrorCallback error_callback_;
@@ -118,6 +127,9 @@
// This method must be thread-safe, and must cause concurrent Reads/Writes to terminate.
// Formerly known as 'Kick' in atransport.
virtual void Close() = 0;
+
+ // Terminate a connection, and reset it.
+ virtual void Reset() = 0;
};
struct BlockingConnectionAdapter : public Connection {
@@ -130,6 +142,8 @@
virtual void Start() override final;
virtual void Stop() override final;
+ virtual void Reset() override final;
+
bool started_ GUARDED_BY(mutex_) = false;
bool stopped_ GUARDED_BY(mutex_) = false;
@@ -151,6 +165,7 @@
bool Write(apacket* packet) override final;
void Close() override;
+ virtual void Reset() override final { Close(); }
private:
unique_fd fd_;
@@ -164,6 +179,7 @@
bool Write(apacket* packet) override final;
void Close() override final;
+ virtual void Reset() override final;
usb_handle* handle_;
};
@@ -229,6 +245,7 @@
virtual ~atransport();
int Write(apacket* p);
+ void Reset();
void Kick();
bool kicked() const { return kicked_; }
@@ -242,6 +259,9 @@
return connection_;
}
+ void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
+ usb_handle* GetUsbHandle() { return usb_handle_; }
+
const TransportId id;
size_t ref_count = 0;
bool online = false;
@@ -258,6 +278,7 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
+ void ResetKeys();
#endif
char token[TOKEN_SIZE] = {};
@@ -332,6 +353,9 @@
// The underlying connection object.
std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+ // USB handle for the connection, if available.
+ usb_handle* usb_handle_ = nullptr;
+
// A callback that will be invoked when the atransport needs to reconnect.
ReconnectCallback reconnect_;
@@ -351,7 +375,7 @@
atransport* acquire_one_transport(TransportType type, const char* serial, TransportId transport_id,
bool* is_ambiguous, std::string* error_out,
bool accept_any_state = false);
-void kick_transport(atransport* t);
+void kick_transport(atransport* t, bool reset = false);
void update_transports(void);
// Iterates across all of the current and pending transports.
@@ -382,11 +406,22 @@
bool check_header(apacket* p, atransport* t);
-void close_usb_devices();
-void close_usb_devices(std::function<bool(const atransport*)> predicate);
+void close_usb_devices(bool reset = false);
+void close_usb_devices(std::function<bool(const atransport*)> predicate, bool reset = false);
void send_packet(apacket* p, atransport* t);
asocket* create_device_tracker(bool long_output);
+#if !ADB_HOST
+unique_fd tcp_listen_inaddr_any(int port, std::string* error);
+void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port);
+
+#if defined(__ANDROID__)
+void qemu_socket_thread(int port);
+bool use_qemu_goldfish();
+#endif
+
+#endif
+
#endif /* __TRANSPORT_H */
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index ec61279..a93e68a 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -85,18 +85,9 @@
if (pfds[0].revents) {
if ((pfds[0].revents & POLLOUT)) {
std::lock_guard<std::mutex> lock(this->write_mutex_);
- WriteResult result = DispatchWrites();
- switch (result) {
- case WriteResult::Error:
- *error = "write failed";
- return;
-
- case WriteResult::Completed:
- writable_ = true;
- break;
-
- case WriteResult::TryAgain:
- break;
+ if (DispatchWrites() == WriteResult::Error) {
+ *error = "write failed";
+ return;
}
}
@@ -179,13 +170,14 @@
WriteResult DispatchWrites() REQUIRES(write_mutex_) {
CHECK(!write_buffer_.empty());
- if (!writable_) {
- return WriteResult::TryAgain;
- }
-
auto iovs = write_buffer_.iovecs();
ssize_t rc = adb_writev(fd_.get(), iovs.data(), iovs.size());
if (rc == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ writable_ = false;
+ return WriteResult::TryAgain;
+ }
+
return WriteResult::Error;
} else if (rc == 0) {
errno = 0;
@@ -194,6 +186,7 @@
// TODO: Implement a more efficient drop_front?
write_buffer_.take_front(rc);
+ writable_ = write_buffer_.empty();
if (write_buffer_.empty()) {
return WriteResult::Completed;
}
@@ -211,7 +204,12 @@
if (!packet->payload.empty()) {
write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
}
- return DispatchWrites() != WriteResult::Error;
+
+ WriteResult result = DispatchWrites();
+ if (result == WriteResult::TryAgain) {
+ WakeThread();
+ }
+ return result != WriteResult::Error;
}
std::thread thread_;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index dc87ac7..b9f738d 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -26,6 +26,7 @@
#include <sys/types.h>
#include <condition_variable>
+#include <functional>
#include <memory>
#include <mutex>
#include <thread>
@@ -45,18 +46,34 @@
#include "adb_io.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
+#include "socket_spec.h"
#include "sysdeps/chrono.h"
#if ADB_HOST
// Android Wear has been using port 5601 in all of its documentation/tooling,
// but we search for emulators on ports [5554, 5555 + ADB_LOCAL_TRANSPORT_MAX].
-// Avoid stomping on their port by limiting the number of emulators that can be
-// connected.
-#define ADB_LOCAL_TRANSPORT_MAX 16
+// Avoid stomping on their port by restricting the active scanning range.
+// Once emulators self-(re-)register, they'll have to avoid 5601 in their own way.
+static int adb_local_transport_max_port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT + 16 * 2 - 1;
static std::mutex& local_transports_lock = *new std::mutex();
+static void adb_local_transport_max_port_env_override() {
+ const char* env_max_s = getenv("ADB_LOCAL_TRANSPORT_MAX_PORT");
+ if (env_max_s != nullptr) {
+ size_t env_max;
+ if (ParseUint(&env_max, env_max_s, nullptr) && env_max < 65536) {
+ // < DEFAULT_ADB_LOCAL_TRANSPORT_PORT harmlessly mimics ADB_EMU=0
+ adb_local_transport_max_port = env_max;
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT read as %d", adb_local_transport_max_port);
+ } else {
+ D("transport: ADB_LOCAL_TRANSPORT_MAX_PORT '%s' invalid or >= 65536, so ignored",
+ env_max_s);
+ }
+ }
+}
+
// We keep a map from emulator port to transport.
// TODO: weak_ptr?
static auto& local_transports GUARDED_BY(local_transports_lock) =
@@ -70,32 +87,18 @@
std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
std::string* response) {
- std::string serial;
- std::string host;
+ unique_fd fd;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
- D("failed to parse address: '%s'", address.c_str());
- return std::make_tuple(unique_fd(), port, serial);
- }
-
- std::string error;
- unique_fd fd(network_connect(host.c_str(), port, SOCK_STREAM, 10, &error));
- if (fd == -1) {
- *response = android::base::StringPrintf("unable to connect to %s: %s",
- serial.c_str(), error.c_str());
+ std::string serial;
+ std::string prefix_addr = address.starts_with("vsock:") ? address : "tcp:" + address;
+ if (socket_spec_connect(&fd, prefix_addr, &port, &serial, response)) {
+ close_on_exec(fd);
+ if (!set_tcp_keepalive(fd, 1)) {
+ D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
+ }
return std::make_tuple(std::move(fd), port, serial);
}
-
- D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
- close_on_exec(fd);
- disable_tcp_nagle(fd);
-
- // Send a TCP keepalive ping to the device every second so we can detect disconnects.
- if (!set_tcp_keepalive(fd, 1)) {
- D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
- }
-
- return std::make_tuple(std::move(fd), port, serial);
+ return std::make_tuple(unique_fd(), 0, serial);
}
void connect_device(const std::string& address, std::string* response) {
@@ -109,6 +112,9 @@
int port;
std::string serial;
std::tie(fd, port, serial) = tcp_connect(address, response);
+ if (fd.get() == -1) {
+ return;
+ }
auto reconnect = [address](atransport* t) {
std::string response;
unique_fd fd;
@@ -119,7 +125,6 @@
D("reconnect failed: %s", response.c_str());
return ReconnectResult::Retry;
}
-
// This invokes the part of register_socket_transport() that needs to be
// invoked if the atransport* has already been setup. This eventually
// calls atransport->SetConnection() with a newly created Connection*
@@ -177,12 +182,10 @@
#if ADB_HOST
static void PollAllLocalPortsForEmulator() {
- int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
- int count = ADB_LOCAL_TRANSPORT_MAX;
-
// Try to connect to any number of running emulator instances.
- for ( ; count > 0; count--, port += 2 ) {
- local_connect(port);
+ for (int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; port <= adb_local_transport_max_port;
+ port += 2) {
+ local_connect(port); // Note, uses port and port-1, so '=max_port' is OK.
}
}
@@ -243,17 +246,21 @@
}
}
-#else // ADB_HOST
+#else // !ADB_HOST
-static void server_socket_thread(int port) {
- unique_fd serverfd;
-
+void server_socket_thread(std::function<unique_fd(int, std::string*)> listen_func, int port) {
adb_thread_setname("server socket");
- D("transport: server_socket_thread() starting");
+
+ unique_fd serverfd;
+ std::string error;
+
while (serverfd == -1) {
- std::string error;
- serverfd.reset(network_inaddr_any_server(port, SOCK_STREAM, &error));
- if (serverfd < 0) {
+ errno = 0;
+ serverfd = listen_func(port, &error);
+ if (errno == EAFNOSUPPORT || errno == EINVAL || errno == EPROTONOSUPPORT) {
+ D("unrecoverable error: '%s'", error.c_str());
+ return;
+ } else if (serverfd < 0) {
D("server: cannot bind socket yet: %s", error.c_str());
std::this_thread::sleep_for(1s);
continue;
@@ -262,7 +269,7 @@
}
while (true) {
- D("server: trying to get new connection from %d", port);
+ D("server: trying to get new connection from fd %d", serverfd.get());
unique_fd fd(adb_socket_accept(serverfd, nullptr, nullptr));
if (fd >= 0) {
D("server: new connection on fd %d", fd.get());
@@ -276,152 +283,41 @@
D("transport: server_socket_thread() exiting");
}
-/* This is relevant only for ADB daemon running inside the emulator. */
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redefine them back after qemu_pipe.h inclusion.
- */
-#undef open
-#undef read
-#undef write
-#define open adb_open
-#define read adb_read
-#define write adb_write
-#include <qemu_pipe.h>
-#undef open
-#undef read
-#undef write
-#define open ___xxx_open
-#define read ___xxx_read
-#define write ___xxx_write
+#endif
-/* A worker thread that monitors host connections, and registers a transport for
- * every new host connection. This thread replaces server_socket_thread on
- * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD
- * pipe to communicate with adbd daemon inside the guest. This is done in order
- * to provide more robust communication channel between ADB host and guest. The
- * main issue with server_socket_thread approach is that it runs on top of TCP,
- * and thus is sensitive to network disruptions. For instance, the
- * ConnectionManager may decide to reset all network connections, in which case
- * the connection between ADB host and guest will be lost. To make ADB traffic
- * independent from the network, we use here 'adb' QEMUD service to transfer data
- * between the host, and the guest. See external/qemu/android/adb-*.* that
- * implements the emulator's side of the protocol. Another advantage of using
- * QEMUD approach is that ADB will be up much sooner, since it doesn't depend
- * anymore on network being set up.
- * The guest side of the protocol contains the following phases:
- * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service
- * is opened, and it becomes clear whether or not emulator supports that
- * protocol.
- * - Wait for the ADB host to create connection with the guest. This is done by
- * sending an 'accept' request to the adb QEMUD service, and waiting on
- * response.
- * - When new ADB host connection is accepted, the connection with adb QEMUD
- * service is registered as the transport, and a 'start' request is sent to the
- * adb QEMUD service, indicating that the guest is ready to receive messages.
- * Note that the guest will ignore messages sent down from the emulator before
- * the transport registration is completed. That's why we need to send the
- * 'start' request after the transport is registered.
- */
-static void qemu_socket_thread(int port) {
- /* 'accept' request to the adb QEMUD service. */
- static const char _accept_req[] = "accept";
- /* 'start' request to the adb QEMUD service. */
- static const char _start_req[] = "start";
- /* 'ok' reply from the adb QEMUD service. */
- static const char _ok_resp[] = "ok";
-
- char tmp[256];
- char con_name[32];
-
- adb_thread_setname("qemu socket");
- D("transport: qemu_socket_thread() starting");
-
- /* adb QEMUD service connection request. */
- snprintf(con_name, sizeof(con_name), "pipe:qemud:adb:%d", port);
-
- /* Connect to the adb QEMUD service. */
- unique_fd fd(qemu_pipe_open(con_name));
- if (fd < 0) {
- /* This could be an older version of the emulator, that doesn't
- * implement adb QEMUD service. Fall back to the old TCP way. */
- D("adb service is not available. Falling back to TCP socket.");
- std::thread(server_socket_thread, port).detach();
- return;
- }
-
- while (true) {
- /*
- * Wait till the host creates a new connection.
- */
-
- /* Send the 'accept' request. */
- if (WriteFdExactly(fd.get(), _accept_req, strlen(_accept_req))) {
- /* Wait for the response. In the response we expect 'ok' on success,
- * or 'ko' on failure. */
- if (!ReadFdExactly(fd.get(), tmp, 2) || memcmp(tmp, _ok_resp, 2)) {
- D("Accepting ADB host connection has failed.");
- } else {
- /* Host is connected. Register the transport, and start the
- * exchange. */
- std::string serial = android::base::StringPrintf("host-%d", fd.get());
- WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
- register_socket_transport(std::move(fd), std::move(serial), port, 1,
- [](atransport*) { return ReconnectResult::Abort; });
- }
-
- /* Prepare for accepting of the next ADB host connection. */
- fd.reset(qemu_pipe_open(con_name));
- if (fd < 0) {
- D("adb service become unavailable.");
- return;
- }
- } else {
- D("Unable to send the '%s' request to ADB service.", _accept_req);
- return;
- }
- }
- D("transport: qemu_socket_thread() exiting");
- return;
+unique_fd tcp_listen_inaddr_any(int port, std::string* error) {
+ return unique_fd{network_inaddr_any_server(port, SOCK_STREAM, error)};
}
-// If adbd is running inside the emulator, it will normally use QEMUD pipe (aka
-// goldfish) as the transport. This can either be explicitly set by the
-// service.adb.transport property, or be inferred from ro.kernel.qemu that is
-// set to "1" for ranchu/goldfish.
-static bool use_qemu_goldfish() {
- // Legacy way to detect if adbd should use the goldfish pipe is to check for
- // ro.kernel.qemu, keep that behaviour for backward compatibility.
- if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
- return true;
- }
- // If service.adb.transport is present and is set to "goldfish", use the
- // QEMUD pipe.
- if (android::base::GetProperty("service.adb.transport", "") == "goldfish") {
- return true;
- }
- return false;
+#if !ADB_HOST
+static unique_fd vsock_listen(int port, std::string* error) {
+ return unique_fd{
+ socket_spec_listen(android::base::StringPrintf("vsock:%d", port), error, nullptr)
+ };
}
+#endif
-#endif // !ADB_HOST
-
-void local_init(int port)
-{
- void (*func)(int);
- const char* debug_name = "";
-
+void local_init(int port) {
#if ADB_HOST
- func = client_socket_thread;
- debug_name = "client";
+ D("transport: local client init");
+ std::thread(client_socket_thread, port).detach();
+ adb_local_transport_max_port_env_override();
+#elif !defined(__ANDROID__)
+ // Host adbd.
+ D("transport: local server init");
+ std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+ std::thread(server_socket_thread, vsock_listen, port).detach();
#else
+ D("transport: local server init");
// For the adbd daemon in the system image we need to distinguish
// between the device, and the emulator.
- func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
- debug_name = "server";
+ if (use_qemu_goldfish()) {
+ std::thread(qemu_socket_thread, port).detach();
+ } else {
+ std::thread(server_socket_thread, tcp_listen_inaddr_any, port).detach();
+ }
+ std::thread(server_socket_thread, vsock_listen, port).detach();
#endif // !ADB_HOST
-
- D("transport: local %s init", debug_name);
- std::thread(func, port).detach();
}
#if ADB_HOST
@@ -488,10 +384,6 @@
if (existing_transport != nullptr) {
D("local transport for port %d already registered (%p)?", adb_port, existing_transport);
fail = -1;
- } else if (local_transports.size() >= ADB_LOCAL_TRANSPORT_MAX) {
- // Too many emulators.
- D("cannot register more emulators. Maximum is %d", ADB_LOCAL_TRANSPORT_MAX);
- fail = -1;
} else {
local_transports[adb_port] = t;
}
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index b66f8fa..00beb3a 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
#include "adb.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
struct TransportTest : public FdeventTest {};
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index c471bf9..3e87522 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -171,6 +171,11 @@
return true;
}
+void UsbConnection::Reset() {
+ usb_reset(handle_);
+ usb_kick(handle_);
+}
+
void UsbConnection::Close() {
usb_kick(handle_);
}
@@ -180,6 +185,7 @@
auto connection = std::make_unique<UsbConnection>(h);
t->SetConnection(std::make_unique<BlockingConnectionAdapter>(std::move(connection)));
t->type = kTransportUsb;
+ t->SetUsbHandle(h);
}
int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol) {
diff --git a/adb/types.h b/adb/types.h
index 0c71c3a..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -108,7 +108,10 @@
CHECK_EQ(0ULL, capacity_);
CHECK_EQ(0ULL, size_);
if (size != 0) {
- data_ = std::make_unique<char[]>(size);
+ // This isn't std::make_unique because that's equivalent to `new char[size]()`, which
+ // value-initializes the array instead of leaving it uninitialized. As an optimization,
+ // call new without parentheses to avoid this costly initialization.
+ data_.reset(new char[size]);
capacity_ = size;
size_ = size;
}
@@ -213,7 +216,10 @@
// Add a nonempty block to the chain.
// The end of the chain must be a complete block (i.e. end_offset_ == 0).
void append(std::unique_ptr<const block_type> block) {
- CHECK_NE(0ULL, block->size());
+ if (block->size() == 0) {
+ return;
+ }
+
CHECK_EQ(0ULL, end_offset_);
chain_length_ += block->size();
chain_.emplace_back(std::move(block));
diff --git a/adb/usb.h b/adb/usb.h
index cd83c42..eb8ca6c 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -26,6 +26,7 @@
int usb_write(handle_ref_type h, const void* data, int len); \
int usb_read(handle_ref_type h, void* data, int len); \
int usb_close(handle_ref_type h); \
+ void usb_reset(handle_ref_type h); \
void usb_kick(handle_ref_type h); \
size_t usb_get_max_packet_size(handle_ref_type)
diff --git a/base/Android.bp b/base/Android.bp
index 741664b..357ce01 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,6 +20,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-D_FILE_OFFSET_BITS=64",
],
}
@@ -28,6 +29,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
@@ -44,11 +46,14 @@
name: "libbase_defaults",
defaults: ["libbase_cflags_defaults"],
srcs: [
+ "abi_compatibility.cpp",
"chrono_utils.cpp",
+ "cmsg.cpp",
"file.cpp",
"logging.cpp",
"mapped_file.cpp",
"parsenetaddress.cpp",
+ "process.cpp",
"properties.cpp",
"quick_exit.cpp",
"stringprintf.cpp",
@@ -84,6 +89,9 @@
"errors_windows.cpp",
"utf8.cpp",
],
+ exclude_srcs: [
+ "cmsg.cpp",
+ ],
enabled: true,
},
},
@@ -95,6 +103,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -103,6 +112,9 @@
"libbase_headers",
],
export_header_lib_headers: ["libbase_headers"],
+ static_libs: ["fmtlib"],
+ whole_static_libs: ["fmtlib"],
+ export_static_lib_headers: ["fmtlib"],
}
cc_library_static {
@@ -111,6 +123,9 @@
sdk_version: "current",
stl: "c++_static",
export_include_dirs: ["include"],
+ static_libs: ["fmtlib_ndk"],
+ whole_static_libs: ["fmtlib_ndk"],
+ export_static_lib_headers: ["fmtlib_ndk"],
}
// Tests
@@ -120,8 +135,10 @@
defaults: ["libbase_cflags_defaults"],
host_supported: true,
srcs: [
+ "cmsg_test.cpp",
"endian_test.cpp",
"errors_test.cpp",
+ "expected_test.cpp",
"file_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
@@ -129,8 +146,10 @@
"parsedouble_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
+ "process_test.cpp",
"properties_test.cpp",
"quick_exit_test.cpp",
+ "result_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
@@ -165,3 +184,21 @@
},
test_suites: ["device-tests"],
}
+
+cc_benchmark {
+ name: "libbase_benchmark",
+ defaults: ["libbase_cflags_defaults"],
+
+ srcs: ["format_benchmark.cpp"],
+ shared_libs: ["libbase"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
new file mode 100644
index 0000000..06a7801
--- /dev/null
+++ b/base/abi_compatibility.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include "android-base/cmsg.h"
+#include "android-base/file.h"
+#include "android-base/mapped_file.h"
+#include "android-base/unique_fd.h"
+
+namespace android {
+namespace base {
+
+// These ABI-compatibility shims are in a separate file for two reasons:
+// 1. If they were in the file with the actual functions, it prevents calls to
+// those functions by other functions in the file, due to ambiguity.
+// 2. We will hopefully be able to delete these quickly.
+
+#if !defined(_WIN32)
+ssize_t SendFileDescriptorVector(int sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ return SendFileDescriptorVector(borrowed_fd(sockfd), data, len, fds);
+}
+
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ return ReceiveFileDescriptorVector(borrowed_fd(sockfd), data, len, max_fds, fds);
+}
+#endif
+
+bool ReadFdToString(int fd, std::string* content) {
+ return ReadFdToString(borrowed_fd(fd), content);
+}
+
+bool WriteStringToFd(const std::string& content, int fd) {
+ return WriteStringToFd(content, borrowed_fd(fd));
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+ return ReadFully(borrowed_fd(fd), data, byte_count);
+}
+
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+ return ReadFullyAtOffset(borrowed_fd(fd), data, byte_count, offset);
+}
+
+bool WriteFully(int fd, const void* data, size_t byte_count) {
+ return WriteFully(borrowed_fd(fd), data, byte_count);
+}
+
+#if defined(__LP64__)
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEilmi
+#else
+#define MAPPEDFILE_FROMFD _ZN10MappedFile6FromFdEixmi
+#endif
+
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+extern "C" std::unique_ptr<MappedFile> MAPPEDFILE_FROMFD(int fd, off64_t offset, size_t length,
+ int prot) {
+ return MappedFile::FromFd(fd, offset, length, prot);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg.cpp b/base/cmsg.cpp
new file mode 100644
index 0000000..1fa873c
--- /dev/null
+++ b/base/cmsg.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/user.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace base {
+
+ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
+ const std::vector<int>& fds) {
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * fds.size());
+ size_t cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = cmsg_len;
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ for (size_t i = 0; i < fds.size(); ++i) {
+ cmsg_fds[i] = fds[i];
+ }
+
+#if defined(__linux__)
+ int flags = MSG_NOSIGNAL;
+#else
+ int flags = 0;
+#endif
+
+ return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
+}
+
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
+ std::vector<unique_fd>* fds) {
+ fds->clear();
+
+ size_t cmsg_space = CMSG_SPACE(sizeof(int) * max_fds);
+ if (cmsg_space >= PAGE_SIZE) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ alignas(struct cmsghdr) char cmsg_buf[cmsg_space];
+ iovec iov = {.iov_base = const_cast<void*>(data), .iov_len = len};
+ msghdr msg = {
+ .msg_name = nullptr,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = cmsg_buf,
+ // We can't cast to the actual type of the field, because it's different across platforms.
+ .msg_controllen = static_cast<unsigned int>(cmsg_space),
+ .msg_flags = 0,
+ };
+
+ int flags = MSG_TRUNC | MSG_CTRUNC;
+#if defined(__linux__)
+ flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
+#endif
+
+ ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
+
+ if (rc == -1) {
+ return -1;
+ }
+
+ int error = 0;
+ if ((msg.msg_flags & MSG_TRUNC)) {
+ LOG(ERROR) << "message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ } else if ((msg.msg_flags & MSG_CTRUNC)) {
+ LOG(ERROR) << "control message was truncated when receiving file descriptors";
+ error = EMSGSIZE;
+ }
+
+ std::vector<unique_fd> received_fds;
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ LOG(ERROR) << "received unexpected cmsg: [" << cmsg->cmsg_level << ", " << cmsg->cmsg_type
+ << "]";
+ error = EBADMSG;
+ continue;
+ }
+
+ // There isn't a macro that does the inverse of CMSG_LEN, so hack around it ourselves, with
+ // some asserts to ensure that CMSG_LEN behaves as we expect.
+#if defined(__linux__)
+#define CMSG_ASSERT static_assert
+#else
+// CMSG_LEN is somehow not constexpr on darwin.
+#define CMSG_ASSERT CHECK
+#endif
+ CMSG_ASSERT(CMSG_LEN(0) + 1 * sizeof(int) == CMSG_LEN(1 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 2 * sizeof(int) == CMSG_LEN(2 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 3 * sizeof(int) == CMSG_LEN(3 * sizeof(int)));
+ CMSG_ASSERT(CMSG_LEN(0) + 4 * sizeof(int) == CMSG_LEN(4 * sizeof(int)));
+
+ if (cmsg->cmsg_len % sizeof(int) != 0) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not aligned to sizeof(int)";
+ } else if (cmsg->cmsg_len <= CMSG_LEN(0)) {
+ LOG(FATAL) << "cmsg_len(" << cmsg->cmsg_len << ") not long enough to hold any data";
+ }
+
+ int* cmsg_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ size_t cmsg_fdcount = static_cast<size_t>(cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ for (size_t i = 0; i < cmsg_fdcount; ++i) {
+#if !defined(__linux__)
+ // Linux uses MSG_CMSG_CLOEXEC instead of doing this manually.
+ fcntl(cmsg_fds[i], F_SETFD, FD_CLOEXEC);
+#endif
+ received_fds.emplace_back(cmsg_fds[i]);
+ }
+ }
+
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ if (received_fds.size() > max_fds) {
+ LOG(ERROR) << "received too many file descriptors, expected " << fds->size() << ", received "
+ << received_fds.size();
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ *fds = std::move(received_fds);
+ return rc;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp
new file mode 100644
index 0000000..9ee5c82
--- /dev/null
+++ b/base/cmsg_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/cmsg.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#if !defined(_WIN32)
+
+using android::base::ReceiveFileDescriptors;
+using android::base::SendFileDescriptors;
+using android::base::unique_fd;
+
+static ino_t GetInode(int fd) {
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ PLOG(FATAL) << "fstat failed";
+ }
+
+ return st.st_ino;
+}
+
+struct CmsgTest : ::testing::TestWithParam<bool> {
+ bool Seqpacket() { return GetParam(); }
+
+ void SetUp() override {
+ ASSERT_TRUE(
+ android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
+ int dup1 = dup(tmp1.fd);
+ ASSERT_NE(-1, dup1);
+ int dup2 = dup(tmp2.fd);
+ ASSERT_NE(-1, dup2);
+
+ fd1.reset(dup1);
+ fd2.reset(dup2);
+
+ ino1 = GetInode(dup1);
+ ino2 = GetInode(dup2);
+ }
+
+ unique_fd send;
+ unique_fd recv;
+
+ TemporaryFile tmp1;
+ TemporaryFile tmp2;
+
+ unique_fd fd1;
+ unique_fd fd2;
+
+ ino_t ino1;
+ ino_t ino2;
+};
+
+TEST_P(CmsgTest, smoke) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
+ ASSERT_EQ('x', buf[0]);
+ ASSERT_NE(-1, received.get());
+
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, msg_trunc) {
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+
+ ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
+ if (Seqpacket()) {
+ ASSERT_EQ(-1, rc);
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received1.get());
+ ASSERT_EQ(-1, received2.get());
+ } else {
+ ASSERT_EQ(1, rc);
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+ ASSERT_EQ(1, read(recv.get(), buf, 2));
+ }
+}
+
+TEST_P(CmsgTest, msg_ctrunc) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received;
+ ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(EMSGSIZE, errno);
+ ASSERT_EQ(-1, received.get());
+}
+
+TEST_P(CmsgTest, peek) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+
+ char buf[2];
+ ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
+ ASSERT_EQ('a', buf[0]);
+
+ unique_fd received;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
+ ASSERT_EQ(ino1, GetInode(received.get()));
+}
+
+TEST_P(CmsgTest, stream_fd_association) {
+ if (Seqpacket()) {
+ return;
+ }
+
+ // fds are associated with the first byte of the write.
+ ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
+ ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
+ char buf[2];
+ ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
+ ASSERT_EQ(0, memcmp(buf, "ab", 2));
+
+ std::vector<unique_fd> received1;
+ ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_TRUE(received1.empty());
+
+ unique_fd received2;
+ rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
+ ASSERT_EQ(1, rc);
+ ASSERT_EQ('d', buf[0]);
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, multiple_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fd_ordering) {
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
+
+ char buf[2];
+ unique_fd received1, received2;
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
+ ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
+
+ ASSERT_NE(-1, received1.get());
+ ASSERT_NE(-1, received2.get());
+
+ ASSERT_EQ(ino1, GetInode(received1.get()));
+ ASSERT_EQ(ino2, GetInode(received2.get()));
+}
+
+TEST_P(CmsgTest, separate_fds_no_coalescing) {
+ unique_fd sent1(dup(tmp1.fd));
+ unique_fd sent2(dup(tmp2.fd));
+
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
+ ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
+
+ char buf[2];
+ std::vector<unique_fd> received;
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino1, GetInode(received[0].get()));
+
+ ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
+ ASSERT_EQ(1U, received.size());
+ ASSERT_EQ(ino2, GetInode(received[0].get()));
+}
+
+INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
+
+#endif
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
new file mode 100644
index 0000000..a74bc1d
--- /dev/null
+++ b/base/expected_test.cpp
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/expected.h"
+
+#include <cstdio>
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using android::base::expected;
+using android::base::unexpected;
+
+typedef expected<int, int> exp_int;
+typedef expected<double, double> exp_double;
+typedef expected<std::string, std::string> exp_string;
+typedef expected<std::pair<std::string, int>, int> exp_pair;
+typedef expected<void, int> exp_void;
+
+struct T {
+ int a;
+ int b;
+ T() = default;
+ T(int a, int b) noexcept : a(a), b(b) {}
+};
+bool operator==(const T& x, const T& y) {
+ return x.a == y.a && x.b == y.b;
+}
+bool operator!=(const T& x, const T& y) {
+ return x.a != y.a || x.b != y.b;
+}
+
+struct E {
+ std::string message;
+ int cause;
+ E(const std::string& message, int cause) : message(message), cause(cause) {}
+};
+
+typedef expected<T,E> exp_complex;
+
+TEST(Expected, testDefaultConstructible) {
+ exp_int e;
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(0, e.value());
+
+ exp_complex e2;
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(T(0,0), e2.value());
+
+ exp_void e3;
+ EXPECT_TRUE(e3.has_value());
+}
+
+TEST(Expected, testCopyConstructible) {
+ exp_int e;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_void e3;
+ exp_void e4 = e3;
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testMoveConstructible) {
+ exp_int e;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(0, e.value());
+ EXPECT_EQ(0, e2.value());
+
+ exp_string e3(std::string("hello"));
+ exp_string e4 = std::move(e3);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ("", e3.value()); // e3 is moved
+ EXPECT_EQ("hello", e4.value());
+
+ exp_void e5;
+ exp_void e6 = std::move(e5);
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testCopyConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = e;
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testMoveConstructibleFromConvertibleType) {
+ exp_double e = 3.3f;
+ exp_int e2 = std::move(e);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3.3f, e.value());
+ EXPECT_EQ(3, e2.value());
+}
+
+TEST(Expected, testConstructibleFromValue) {
+ exp_int e = 3;
+ exp_double e2 = 5.5f;
+ exp_string e3 = std::string("hello");
+ exp_complex e4 = T(10, 20);
+ exp_void e5 = {};
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_EQ(3, e.value());
+ EXPECT_EQ(5.5f, e2.value());
+ EXPECT_EQ("hello", e3.value());
+ EXPECT_EQ(T(10,20), e4.value());
+}
+
+TEST(Expected, testConstructibleFromMovedValue) {
+ std::string hello = "hello";
+ exp_string e = std::move(hello);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("hello", e.value());
+ EXPECT_EQ("", hello);
+}
+
+TEST(Expected, testConstructibleFromConvertibleValue) {
+ exp_int e = 3.3f; // double to int
+ exp_string e2 = "hello"; // char* to std::string
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ(3, e.value());
+
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("hello", e2.value());
+}
+
+TEST(Expected, testConstructibleFromUnexpected) {
+ exp_int::unexpected_type unexp = unexpected(10);
+ exp_int e = unexp;
+
+ exp_double::unexpected_type unexp2 = unexpected(10.5f);
+ exp_double e2 = unexp2;
+
+ exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
+ exp_string e3 = unexp3;
+
+ exp_void::unexpected_type unexp4 = unexpected(10);
+ exp_void e4 = unexp4;
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testMoveConstructibleFromUnexpected) {
+ exp_int e = unexpected(10);
+ exp_double e2 = unexpected(10.5f);
+ exp_string e3 = unexpected(std::string("error"));
+ exp_void e4 = unexpected(10);
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e.error());
+ EXPECT_EQ(10.5f, e2.error());
+ EXPECT_EQ("error", e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testConstructibleByForwarding) {
+ exp_string e(std::in_place, 5, 'a');
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("aaaaa", e.value());
+
+ exp_string e2({'a', 'b', 'c'});
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ("abc", e2.value());
+
+ exp_pair e3({"hello", 30});
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_EQ("hello",e3->first);
+ EXPECT_EQ(30,e3->second);
+
+ exp_void e4({});
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testDestructible) {
+ bool destroyed = false;
+ struct T {
+ bool* flag_;
+ T(bool* flag) : flag_(flag) {}
+ ~T() { *flag_ = true; }
+ };
+ {
+ expected<T, int> exp = T(&destroyed);
+ }
+ EXPECT_TRUE(destroyed);
+}
+
+TEST(Expected, testAssignable) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e = e2;
+
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(20, e2.value());
+
+ exp_int e3 = 10;
+ exp_int e4 = 20;
+ e3 = std::move(e4);
+
+ EXPECT_EQ(20, e3.value());
+ EXPECT_EQ(20, e4.value());
+
+ exp_void e5 = unexpected(10);
+ ASSERT_FALSE(e5.has_value());
+ exp_void e6;
+ e5 = e6;
+
+ EXPECT_TRUE(e5.has_value());
+ EXPECT_TRUE(e6.has_value());
+}
+
+TEST(Expected, testAssignableFromValue) {
+ exp_int e = 10;
+ e = 20;
+ EXPECT_EQ(20, e.value());
+
+ exp_double e2 = 3.5f;
+ e2 = 10.5f;
+ EXPECT_EQ(10.5f, e2.value());
+
+ exp_string e3 = "hello";
+ e3 = "world";
+ EXPECT_EQ("world", e3.value());
+
+ exp_void e4 = unexpected(10);
+ ASSERT_FALSE(e4.has_value());
+ e4 = {};
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testAssignableFromUnexpected) {
+ exp_int e = 10;
+ e = unexpected(30);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(30, e.error());
+
+ exp_double e2 = 3.5f;
+ e2 = unexpected(10.5f);
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(10.5f, e2.error());
+
+ exp_string e3 = "hello";
+ e3 = unexpected("world");
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_EQ("world", e3.error());
+
+ exp_void e4 = {};
+ e4 = unexpected(10);
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testAssignableFromMovedValue) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = std::move(world);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_EQ("world", e.value());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testAssignableFromMovedUnexpected) {
+ std::string world = "world";
+ exp_string e = "hello";
+ e = unexpected(std::move(world));
+
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ("world", e.error());
+ EXPECT_EQ("", world);
+}
+
+TEST(Expected, testEmplace) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) noexcept : a(a), b(b) {}
+ };
+ expected<T, int> exp;
+ T& t = exp.emplace(3, 10.5f);
+
+ EXPECT_TRUE(exp.has_value());
+ EXPECT_EQ(3, t.a);
+ EXPECT_EQ(10.5f, t.b);
+ EXPECT_EQ(3, exp.value().a);
+ EXPECT_EQ(10.5, exp.value().b);
+
+ exp_void e = unexpected(10);
+ ASSERT_FALSE(e.has_value());
+ e.emplace();
+ EXPECT_TRUE(e.has_value());
+}
+
+TEST(Expected, testSwapExpectedExpected) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ e.swap(e2);
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(20, e.value());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4;
+ e3.swap(e4);
+
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+}
+
+TEST(Expected, testSwapUnexpectedUnexpected) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(20, e.error());
+ EXPECT_EQ(10, e2.error());
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_FALSE(e4.has_value());
+ EXPECT_EQ(20, e3.error());
+ EXPECT_EQ(10, e4.error());
+}
+
+TEST(Expected, testSwapExpectedUnepected) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(30);
+ e.swap(e2);
+ EXPECT_FALSE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(30, e.error());
+ EXPECT_EQ(10, e2.value());
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ e3.swap(e4);
+ EXPECT_FALSE(e3.has_value());
+ EXPECT_TRUE(e4.has_value());
+ EXPECT_EQ(10, e3.error());
+}
+
+TEST(Expected, testDereference) {
+ struct T {
+ int a;
+ double b;
+ T() {}
+ T(int a, double b) : a(a), b(b) {}
+ };
+ expected<T, int> exp = T(3, 10.5f);
+
+ EXPECT_EQ(3, exp->a);
+ EXPECT_EQ(10.5f, exp->b);
+
+ EXPECT_EQ(3, (*exp).a);
+ EXPECT_EQ(10.5f, (*exp).b);
+}
+
+TEST(Expected, testTest) {
+ exp_int e = 10;
+ EXPECT_TRUE(e);
+ EXPECT_TRUE(e.has_value());
+
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e2);
+ EXPECT_FALSE(e2.has_value());
+}
+
+TEST(Expected, testGetValue) {
+ exp_int e = 10;
+ EXPECT_EQ(10, e.value());
+ EXPECT_EQ(10, e.value_or(20));
+
+ exp_int e2 = unexpected(10);
+ EXPECT_EQ(10, e2.error());
+ EXPECT_EQ(20, e2.value_or(20));
+}
+
+TEST(Expected, testSameValues) {
+ exp_int e = 10;
+ exp_int e2 = 10;
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3;
+ exp_void e4;
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentValues) {
+ exp_int e = 10;
+ exp_int e2 = 20;
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+}
+
+TEST(Expected, testValueWithError) {
+ exp_int e = 10;
+ exp_int e2 = unexpected(10);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3;
+ exp_void e4 = unexpected(10);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testSameErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(10);
+ EXPECT_TRUE(e == e2);
+ EXPECT_TRUE(e2 == e);
+ EXPECT_FALSE(e != e2);
+ EXPECT_FALSE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(10);
+ EXPECT_TRUE(e3 == e4);
+ EXPECT_TRUE(e4 == e3);
+ EXPECT_FALSE(e3 != e4);
+ EXPECT_FALSE(e4 != e3);
+}
+
+TEST(Expected, testDifferentErrors) {
+ exp_int e = unexpected(10);
+ exp_int e2 = unexpected(20);
+ EXPECT_FALSE(e == e2);
+ EXPECT_FALSE(e2 == e);
+ EXPECT_TRUE(e != e2);
+ EXPECT_TRUE(e2 != e);
+
+ exp_void e3 = unexpected(10);
+ exp_void e4 = unexpected(20);
+ EXPECT_FALSE(e3 == e4);
+ EXPECT_FALSE(e4 == e3);
+ EXPECT_TRUE(e3 != e4);
+ EXPECT_TRUE(e4 != e3);
+}
+
+TEST(Expected, testCompareWithSameValue) {
+ exp_int e = 10;
+ int value = 10;
+ EXPECT_TRUE(e == value);
+ EXPECT_TRUE(value == e);
+ EXPECT_FALSE(e != value);
+ EXPECT_FALSE(value != e);
+}
+
+TEST(Expected, testCompareWithDifferentValue) {
+ exp_int e = 10;
+ int value = 20;
+ EXPECT_FALSE(e == value);
+ EXPECT_FALSE(value == e);
+ EXPECT_TRUE(e != value);
+ EXPECT_TRUE(value != e);
+}
+
+TEST(Expected, testCompareWithSameError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 10;
+ EXPECT_TRUE(e == error);
+ EXPECT_TRUE(error == e);
+ EXPECT_FALSE(e != error);
+ EXPECT_FALSE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 10;
+ EXPECT_TRUE(e2 == error2);
+ EXPECT_TRUE(error2 == e2);
+ EXPECT_FALSE(e2 != error2);
+ EXPECT_FALSE(error2 != e2);
+}
+
+TEST(Expected, testCompareWithDifferentError) {
+ exp_int e = unexpected(10);
+ exp_int::unexpected_type error = 20;
+ EXPECT_FALSE(e == error);
+ EXPECT_FALSE(error == e);
+ EXPECT_TRUE(e != error);
+ EXPECT_TRUE(error != e);
+
+ exp_void e2 = unexpected(10);
+ exp_void::unexpected_type error2 = 20;
+ EXPECT_FALSE(e2 == error2);
+ EXPECT_FALSE(error2 == e2);
+ EXPECT_TRUE(e2 != error2);
+ EXPECT_TRUE(error2 != e2);
+}
+
+TEST(Expected, testCompareDifferentType) {
+ expected<int,int> e = 10;
+ expected<int32_t, int> e2 = 10;
+ EXPECT_TRUE(e == e2);
+ e2 = 20;
+ EXPECT_FALSE(e == e2);
+
+ expected<std::string_view,int> e3 = "hello";
+ expected<std::string,int> e4 = "hello";
+ EXPECT_TRUE(e3 == e4);
+ e4 = "world";
+ EXPECT_FALSE(e3 == e4);
+
+ expected<void,int> e5;
+ expected<int,int> e6 = 10;
+ EXPECT_FALSE(e5 == e6);
+ EXPECT_FALSE(e6 == e5);
+}
+
+TEST(Expected, testDivideExample) {
+ struct QR {
+ int quotient;
+ int remainder;
+ QR(int q, int r) noexcept : quotient(q), remainder(r) {}
+ bool operator==(const QR& rhs) const {
+ return quotient == rhs.quotient && remainder == rhs.remainder;
+ }
+ bool operator!=(const QR& rhs) const {
+ return quotient != rhs.quotient || remainder == rhs.remainder;
+ }
+ };
+
+ auto divide = [](int x, int y) -> expected<QR,E> {
+ if (y == 0) {
+ return unexpected(E("divide by zero", -1));
+ } else {
+ return QR(x / y, x % y);
+ }
+ };
+
+ EXPECT_FALSE(divide(10, 0));
+ EXPECT_EQ("divide by zero", divide(10, 0).error().message);
+ EXPECT_EQ(-1, divide(10, 0).error().cause);
+
+ EXPECT_TRUE(divide(10, 3));
+ EXPECT_EQ(QR(3, 1), divide(10, 3));
+}
+
+TEST(Expected, testPair) {
+ auto test = [](bool yes) -> exp_pair {
+ if (yes) {
+ return exp_pair({"yes", 42});
+ } else {
+ return unexpected(42);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r);
+ EXPECT_EQ("yes", r->first);
+}
+
+TEST(Expected, testVoid) {
+ auto test = [](bool ok) -> exp_void {
+ if (ok) {
+ return {};
+ } else {
+ return unexpected(10);
+ }
+ };
+
+ auto r = test(true);
+ EXPECT_TRUE(r);
+ r = test(false);
+ EXPECT_FALSE(r);
+ EXPECT_EQ(10, r.error());
+}
+
+// copied from result_test.cpp
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T,
+ typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+ static void Reset() {
+ constructor_called = 0;
+ copy_constructor_called = 0;
+ move_constructor_called = 0;
+ copy_assignment_called = 0;
+ move_assignment_called = 0;
+ }
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+typedef expected<ConstructorTracker, int> exp_track;
+
+TEST(Expected, testNumberOfCopies) {
+ // default constructor
+ ConstructorTracker::Reset();
+ exp_track e("hello");
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy constructor
+ ConstructorTracker::Reset();
+ exp_track e2 = e;
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move constructor
+ ConstructorTracker::Reset();
+ exp_track e3 = std::move(e);
+ EXPECT_EQ(0U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from lvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct = "hello";
+ exp_track e4(ct);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // construct from rvalue
+ ConstructorTracker::Reset();
+ ConstructorTracker ct2 = "hello";
+ exp_track e5(std::move(ct2));
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // copy assignment
+ ConstructorTracker::Reset();
+ exp_track e6 = "hello";
+ exp_track e7 = "world";
+ e7 = e6;
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ // move assignment
+ ConstructorTracker::Reset();
+ exp_track e8 = "hello";
+ exp_track e9 = "world";
+ e9 = std::move(e8);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
+
+ // swap
+ ConstructorTracker::Reset();
+ exp_track e10 = "hello";
+ exp_track e11 = "world";
+ std::swap(e10, e11);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNoCopyOnReturn) {
+ auto test = [](const std::string& in) -> exp_track {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+ };
+
+ ConstructorTracker::Reset();
+ auto result1 = test("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result2 = test("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ ConstructorTracker::Reset();
+ auto result3 = test("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+TEST(Expected, testNested) {
+ expected<exp_string, std::string> e = "hello";
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e.value().has_value());
+ EXPECT_TRUE(e);
+ EXPECT_TRUE(*e);
+ EXPECT_EQ("hello", e.value().value());
+
+ expected<exp_string, std::string> e2 = unexpected("world");
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_FALSE(e2);
+ EXPECT_EQ("world", e2.error());
+
+ expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
+ EXPECT_TRUE(e3.has_value());
+ EXPECT_FALSE(e3.value().has_value());
+ EXPECT_TRUE(e3);
+ EXPECT_FALSE(*e3);
+ EXPECT_EQ("world", e3.value().error());
+}
+
+constexpr bool equals(const char* a, const char* b) {
+ return (a == nullptr && b == nullptr) ||
+ (a != nullptr && b != nullptr && *a == *b &&
+ (*a == '\0' || equals(a + 1, b + 1)));
+}
+
+TEST(Expected, testConstexpr) {
+ // Compliation error will occur if these expressions can't be
+ // evaluated at compile time
+ constexpr exp_int e(3);
+ constexpr exp_int::unexpected_type err(3);
+ constexpr int i = 4;
+
+ // default constructor
+ static_assert(exp_int().value() == 0);
+ // copy constructor
+ static_assert(exp_int(e).value() == 3);
+ // move constructor
+ static_assert(exp_int(exp_int(4)).value() == 4);
+ // copy construct from value
+ static_assert(exp_int(i).value() == 4);
+ // copy construct from unexpected
+ static_assert(exp_int(err).error() == 3);
+ // move costruct from unexpected
+ static_assert(exp_int(unexpected(3)).error() == 3);
+ // observers
+ static_assert(*exp_int(3) == 3);
+ static_assert(exp_int(3).has_value() == true);
+ static_assert(exp_int(3).value_or(4) == 3);
+
+ typedef expected<const char*, int> exp_s;
+ constexpr exp_s s("hello");
+ constexpr const char* c = "hello";
+ static_assert(equals(exp_s().value(), nullptr));
+ static_assert(equals(exp_s(s).value(), "hello"));
+ static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
+ static_assert(equals(exp_s("hello").value(), "hello"));
+ static_assert(equals(exp_s(c).value(), "hello"));
+}
+
+TEST(Expected, testWithNonConstructible) {
+ struct AssertNotConstructed {
+ AssertNotConstructed() = delete;
+ };
+
+ expected<int, AssertNotConstructed> v(42);
+ EXPECT_TRUE(v.has_value());
+ EXPECT_EQ(42, v.value());
+
+ expected<AssertNotConstructed, int> e(unexpected(42));
+ EXPECT_FALSE(e.has_value());
+ EXPECT_EQ(42, e.error());
+}
+
+TEST(Expected, testWithMoveOnlyType) {
+ typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
+ exp_ptr e(std::make_unique<int>(3));
+ exp_ptr e2(unexpected(std::make_unique<int>(4)));
+
+ EXPECT_TRUE(e.has_value());
+ EXPECT_FALSE(e2.has_value());
+ EXPECT_EQ(3, *(e.value()));
+ EXPECT_EQ(4, *(e2.error()));
+
+ e2 = std::move(e);
+ EXPECT_TRUE(e.has_value());
+ EXPECT_TRUE(e2.has_value());
+ EXPECT_EQ(3, *(e2.value()));
+}
diff --git a/base/file.cpp b/base/file.cpp
index 3834ed4..3dfcfbb 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,7 +18,11 @@
#include <errno.h>
#include <fcntl.h>
+#include <ftw.h>
#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -28,39 +32,164 @@
#include <string>
#include <vector>
-#include "android-base/logging.h"
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#if defined(_WIN32)
+#include <direct.h>
#include <windows.h>
#define O_NOFOLLOW 0
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
#endif
+#include "android-base/logging.h" // and must be after windows.h for ERROR
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return -1;
+ }
+ // Use open() to match the close() that TemporaryFile's destructor does.
+ // Use O_BINARY to match base file APIs.
+ return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+ if (_mktemp(template_name) == nullptr) {
+ return nullptr;
+ }
+ if (_mkdir(template_name) == -1) {
+ return nullptr;
+ }
+ return template_name;
+}
+#endif
+
+namespace {
+
+std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/data/local/tmp";
+ if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+ return tmpdir;
+ }
+ // Tests running in app context can't access /data/local/tmp,
+ // so try current directory if /data/local/tmp is not accessible.
+ return ".";
+#elif defined(_WIN32)
+ char tmp_dir[MAX_PATH];
+ DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir); // checks TMP env
+ CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+ CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+ // GetTempPath() returns a path with a trailing slash, but init()
+ // does not expect that, so remove it.
+ CHECK_EQ(tmp_dir[result - 1], '\\');
+ tmp_dir[result - 1] = '\0';
+ return tmp_dir;
+#else
+ const auto* tmpdir = getenv("TMPDIR");
+ if (tmpdir == nullptr) tmpdir = "/tmp";
+ return tmpdir;
+#endif
+}
+
+} // namespace
+
+TemporaryFile::TemporaryFile() {
+ init(GetSystemTempDir());
+}
+
+TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
+ init(tmp_dir);
+}
+
+TemporaryFile::~TemporaryFile() {
+ if (fd != -1) {
+ close(fd);
+ }
+ if (remove_file_) {
+ unlink(path);
+ }
+}
+
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
+void TemporaryFile::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+ fd = mkstemp(path);
+}
+
+TemporaryDir::TemporaryDir() {
+ init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+ if (!remove_dir_and_contents_) return;
+
+ auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
+ switch (file_type) {
+ case FTW_D:
+ case FTW_DP:
+ case FTW_DNR:
+ if (rmdir(child) == -1) {
+ PLOG(ERROR) << "rmdir " << child;
+ }
+ break;
+ case FTW_NS:
+ default:
+ if (rmdir(child) != -1) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+ FALLTHROUGH_INTENDED;
+ case FTW_F:
+ case FTW_SL:
+ case FTW_SLN:
+ if (unlink(child) == -1) {
+ PLOG(ERROR) << "unlink " << child;
+ }
+ break;
+ }
+ return 0;
+ };
+
+ nftw(path, callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+}
+
+bool TemporaryDir::init(const std::string& tmp_dir) {
+ snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+ return (mkdtemp(path) != nullptr);
+}
+
namespace android {
namespace base {
// Versions of standard library APIs that support UTF-8 strings.
using namespace android::base::utf8;
-bool ReadFdToString(int fd, std::string* content) {
+bool ReadFdToString(borrowed_fd fd, std::string* content) {
content->clear();
// Although original we had small files in mind, this code gets used for
// very large files too, where the std::string growth heuristics might not
// be suitable. https://code.google.com/p/android/issues/detail?id=258500.
struct stat sb;
- if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
+ if (fstat(fd.get(), &sb) != -1 && sb.st_size > 0) {
content->reserve(sb.st_size);
}
char buf[BUFSIZ];
ssize_t n;
- while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
content->append(buf, n);
}
return (n == 0) ? true : false;
@@ -77,11 +206,11 @@
return ReadFdToString(fd, content);
}
-bool WriteStringToFd(const std::string& content, int fd) {
+bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
const char* p = content.data();
size_t left = content.size();
while (left > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
if (n == -1) {
return false;
}
@@ -140,11 +269,11 @@
return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
-bool ReadFully(int fd, void* data, size_t byte_count) {
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
uint8_t* p = reinterpret_cast<uint8_t*>(data);
size_t remaining = byte_count;
while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+ ssize_t n = TEMP_FAILURE_RETRY(read(fd.get(), p, remaining));
if (n <= 0) return false;
p += n;
remaining -= n;
@@ -155,14 +284,14 @@
#if defined(_WIN32)
// Windows implementation of pread. Note that this DOES move the file descriptors read position,
// but it does so atomically.
-static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
+static ssize_t pread(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
DWORD bytes_read;
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
- &bytes_read, &overlapped)) {
+ if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
+ static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
// In case someone tries to read errno (since this is masquerading as a POSIX call)
errno = EIO;
return -1;
@@ -171,10 +300,10 @@
}
#endif
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
uint8_t* p = reinterpret_cast<uint8_t*>(data);
while (byte_count > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
+ ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
if (n <= 0) return false;
p += n;
byte_count -= n;
@@ -183,11 +312,11 @@
return true;
}
-bool WriteFully(int fd, const void* data, size_t byte_count) {
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
size_t remaining = byte_count;
while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+ ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
if (n == -1) return false;
p += n;
remaining -= n;
@@ -256,7 +385,12 @@
bool Realpath(const std::string& path, std::string* result) {
result->clear();
- char* realpath_buf = realpath(path.c_str(), nullptr);
+ // realpath may exit with EINTR. Retry if so.
+ char* realpath_buf = nullptr;
+ do {
+ realpath_buf = realpath(path.c_str(), nullptr);
+ } while (realpath_buf == nullptr && errno == EINTR);
+
if (realpath_buf == nullptr) {
return false;
}
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 6794652..f64e81c 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -24,8 +24,6 @@
#include <string>
-#include "android-base/test_utils.h"
-
#if !defined(_WIN32)
#include <pwd.h>
#endif
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
new file mode 100644
index 0000000..9590b23
--- /dev/null
+++ b/base/format_benchmark.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/format.h"
+
+#include <limits>
+
+#include <benchmark/benchmark.h>
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+
+static void BenchmarkFormatInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatInt);
+
+static void BenchmarkStringPrintfInt(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%d %d %d", 42, std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfInt);
+
+static void BenchmarkFormatFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} {} {}", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkFormatFloat);
+
+static void BenchmarkStringPrintfFloat(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%f %f %f", 42.42, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfFloat);
+
+static void BenchmarkFormatStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(fmt::format("{} hello there {}", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkFormatStrings);
+
+static void BenchmarkStringPrintfStrings(benchmark::State& state) {
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(StringPrintf("%s hello there %s", "hi,", "!!"));
+ }
+}
+
+BENCHMARK(BenchmarkStringPrintfStrings);
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/base/include/android-base/cmsg.h b/base/include/android-base/cmsg.h
new file mode 100644
index 0000000..e4197b1
--- /dev/null
+++ b/base/include/android-base/cmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <type_traits>
+#include <vector>
+
+#include <android-base/collections.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace base {
+
+#if !defined(_WIN32)
+
+// Helpers for sending and receiving file descriptors across Unix domain sockets.
+//
+// The cmsg(3) API is very hard to get right, with multiple landmines that can
+// lead to death. Almost all of the uses of cmsg in Android make at least one of
+// the following mistakes:
+//
+// - not aligning the cmsg buffer
+// - leaking fds if more fds are received than expected
+// - blindly dereferencing CMSG_DATA without checking the header
+// - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
+// - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
+// - using a length specified in number of fds instead of bytes
+//
+// These functions wrap the hard-to-use cmsg API with an easier to use abstraction.
+
+// Send file descriptors across a Unix domain socket.
+//
+// Note that the write can return short if the socket type is SOCK_STREAM. When
+// this happens, file descriptors are still sent to the other end, but with
+// truncated data. For this reason, using SOCK_SEQPACKET or SOCK_DGRAM is recommended.
+ssize_t SendFileDescriptorVector(borrowed_fd sock, const void* data, size_t len,
+ const std::vector<int>& fds);
+
+// Receive file descriptors from a Unix domain socket.
+//
+// If more FDs (or bytes, for datagram sockets) are received than expected,
+// -1 is returned with errno set to EMSGSIZE, and all received FDs are thrown away.
+ssize_t ReceiveFileDescriptorVector(borrowed_fd sock, void* data, size_t len, size_t max_fds,
+ std::vector<android::base::unique_fd>* fds);
+
+// Helper for SendFileDescriptorVector that constructs a std::vector for you, e.g.:
+// SendFileDescriptors(sock, "foo", 3, std::move(fd1), std::move(fd2))
+template <typename... Args>
+ssize_t SendFileDescriptors(borrowed_fd sock, const void* data, size_t len, Args&&... sent_fds) {
+ // Do not allow implicit conversion to int: people might try to do something along the lines of:
+ // SendFileDescriptors(..., std::move(a_unique_fd))
+ // and be surprised when the unique_fd isn't closed afterwards.
+ AssertType<int>(std::forward<Args>(sent_fds)...);
+ std::vector<int> fds;
+ Append(fds, std::forward<Args>(sent_fds)...);
+ return SendFileDescriptorVector(sock, data, len, fds);
+}
+
+// Helper for ReceiveFileDescriptorVector that receives an exact number of file descriptors.
+// If more file descriptors are received than requested, -1 is returned with errno set to EMSGSIZE.
+// If fewer file descriptors are received than requested, -1 is returned with errno set to ENOMSG.
+// In both cases, all arguments are cleared and any received FDs are thrown away.
+template <typename... Args>
+ssize_t ReceiveFileDescriptors(borrowed_fd sock, void* data, size_t len, Args&&... received_fds) {
+ std::vector<unique_fd*> fds;
+ Append(fds, std::forward<Args>(received_fds)...);
+
+ std::vector<unique_fd> result;
+ ssize_t rc = ReceiveFileDescriptorVector(sock, data, len, fds.size(), &result);
+ if (rc == -1 || result.size() != fds.size()) {
+ int err = rc == -1 ? errno : ENOMSG;
+ for (unique_fd* fd : fds) {
+ fd->reset();
+ }
+ errno = err;
+ return -1;
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ *fds[i] = std::move(result[i]);
+ }
+ return rc;
+}
+
+#endif
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/collections.h b/base/include/android-base/collections.h
new file mode 100644
index 0000000..be0683a
--- /dev/null
+++ b/base/include/android-base/collections.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utility>
+
+namespace android {
+namespace base {
+
+// Helpers for converting a variadic template parameter pack to a homogeneous collection.
+// Parameters must be implictly convertible to the contained type (including via move/copy ctors).
+//
+// Use as follows:
+//
+// template <typename... Args>
+// std::vector<int> CreateVector(Args&&... args) {
+// std::vector<int> result;
+// Append(result, std::forward<Args>(args)...);
+// return result;
+// }
+template <typename CollectionType, typename T>
+void Append(CollectionType& collection, T&& arg) {
+ collection.push_back(std::forward<T>(arg));
+}
+
+template <typename CollectionType, typename T, typename... Args>
+void Append(CollectionType& collection, T&& arg, Args&&... args) {
+ collection.push_back(std::forward<T>(arg));
+ return Append(collection, std::forward<Args>(args)...);
+}
+
+// Assert that all of the arguments in a variadic template parameter pack are of a given type
+// after std::decay.
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AssertType(Arg&&, Args&&... args) {
+ static_assert(std::is_same<T, typename std::decay<Arg>::type>::value);
+ AssertType<T>(std::forward<Args>(args)...);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
new file mode 100644
index 0000000..030ef35e
--- /dev/null
+++ b/base/include/android-base/expected.h
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+// android::base::expected is an Android implementation of the std::expected
+// proposal.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html
+//
+// Usage:
+// using android::base::expected;
+// using android::base::unexpected;
+//
+// expected<double,std::string> safe_divide(double i, double j) {
+// if (j == 0) return unexpected("divide by zero");
+// else return i / j;
+// }
+//
+// void test() {
+// auto q = safe_divide(10, 0);
+// if (q) { printf("%f\n", q.value()); }
+// else { printf("%s\n", q.error().c_str()); }
+// }
+//
+// When the proposal becomes part of the standard and is implemented by
+// libcxx, this will be removed and android::base::expected will be
+// type alias to std::expected.
+//
+
+namespace android {
+namespace base {
+
+// Synopsis
+template<class T, class E>
+ class expected;
+
+template<class E>
+ class unexpected;
+template<class E>
+ unexpected(E) -> unexpected<E>;
+
+template<class E>
+ class bad_expected_access;
+
+template<>
+ class bad_expected_access<void>;
+
+struct unexpect_t {
+ explicit unexpect_t() = default;
+};
+inline constexpr unexpect_t unexpect{};
+
+// macros for SFINAE
+#define _ENABLE_IF(...) \
+ , std::enable_if_t<(__VA_ARGS__)>* = nullptr
+
+// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
+// ignored when used as a return value. This is off by default.
+#ifdef NODISCARD_EXPECTED
+#define _NODISCARD_ [[nodiscard]]
+#else
+#define _NODISCARD_
+#endif
+
+// Class expected
+template<class T, class E>
+class _NODISCARD_ expected {
+ public:
+ using value_type = T;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ template<class U>
+ using rebind = expected<U, error_type>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ constexpr expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (rhs.has_value()) var_ = rhs.value();
+ else var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ !(!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* non-explicit */
+ )>
+ constexpr expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_constructible_v<T, const U&> &&
+ std::is_constructible_v<E, const G&> &&
+ !std::is_constructible_v<T, expected<U, G>&> &&
+ !std::is_constructible_v<T, expected<U, G>&&> &&
+ !std::is_constructible_v<T, const expected<U, G>&> &&
+ !std::is_constructible_v<T, const expected<U, G>&&> &&
+ !std::is_convertible_v<expected<U, G>&, T> &&
+ !std::is_convertible_v<expected<U, G>&&, T> &&
+ !std::is_convertible_v<const expected<U, G>&, T> &&
+ !std::is_convertible_v<const expected<U, G>&&, T> &&
+ (!std::is_convertible_v<const U&, T> ||
+ !std::is_convertible_v<const G&, E>) /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (rhs.has_value()) var_ = std::move(rhs.value());
+ else var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ std::is_convertible_v<U&&, T> /* non-explicit */
+ )>
+ constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_convertible_v<U&&, T> /* explicit */
+ )>
+ constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<T, Args&&...>
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&... args)
+ : var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) noexcept(
+ std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
+
+ template <class U = T _ENABLE_IF(
+ !std::is_void_v<T> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+ std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
+ std::is_nothrow_move_constructible_v<E>)>
+ expected& operator=(U&& rhs) {
+ var_ = T(std::forward<U>(rhs));
+ return *this;
+ }
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ template<class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, Args...>
+ )>
+ T& emplace(Args&&... args) {
+ expected(std::in_place, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>
+ )>
+ T& emplace(std::initializer_list<U> il, Args&&... args) {
+ expected(std::in_place, il, std::forward<Args>(args)...).swap(*this);
+ return value();
+ }
+
+ // swap
+ template<typename U = T, typename = std::enable_if_t<(
+ std::is_swappable_v<U> &&
+ std::is_swappable_v<E> &&
+ (std::is_move_constructible_v<U> ||
+ std::is_move_constructible_v<E>))>>
+ void swap(expected& rhs) noexcept(
+ std::is_nothrow_move_constructible_v<T> &&
+ std::is_nothrow_swappable_v<T> &&
+ std::is_nothrow_move_constructible_v<E> &&
+ std::is_nothrow_swappable_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr const T* operator->() const { return std::addressof(value()); }
+ constexpr T* operator->() { return std::addressof(value()); }
+ constexpr const T& operator*() const& { return value(); }
+ constexpr T& operator*() & { return value(); }
+ constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
+
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+ constexpr const T& value() const& { return std::get<T>(var_); }
+ constexpr T& value() & { return std::get<T>(var_); }
+ constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
+ constexpr T&& value() && { return std::move(std::get<T>(var_)); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ template<class U _ENABLE_IF(
+ std::is_copy_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) const& {
+ if (has_value()) return value();
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ template<class U _ENABLE_IF(
+ std::is_move_constructible_v<T> &&
+ std::is_convertible_v<U, T>
+ )>
+ constexpr T value_or(U&& v) && {
+ if (has_value()) return std::move(value());
+ else return static_cast<T>(std::forward<U>(v));
+ }
+
+ // expected equality operators
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
+ template<class T1, class E1, class T2, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
+
+ // comparison with T
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator==(const expected<T1, E1>&, const T2&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator==(const T2&, const expected<T1, E1>&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator!=(const expected<T1, E1>&, const T2&);
+ template<class T1, class E1, class T2>
+ friend constexpr bool operator!=(const T2&, const expected<T1, E1>&);
+
+ // Comparison with unexpected<E>
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
+ template<class T1, class E1, class E2>
+ friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<value_type, unexpected_type> var_;
+};
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return *x == *y;
+ }
+}
+
+template<class T1, class E1, class T2, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
+ return !(x == y);
+}
+
+// comparison with T
+template<class T1, class E1, class T2>
+constexpr bool operator==(const expected<T1, E1>& x, const T2& y) {
+ return x.has_value() && (*x == y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator==(const T2& x, const expected<T1, E1>& y) {
+ return y.has_value() && (x == *y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const expected<T1, E1>& x, const T2& y) {
+ return !x.has_value() || (*x != y);
+}
+template<class T1, class E1, class T2>
+constexpr bool operator!=(const T2& x, const expected<T1, E1>& y) {
+ return !y.has_value() || (x != *y);
+}
+
+// Comparison with unexpected<E>
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return !x.has_value() && (x.error() == y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return !y.has_value() && (x.value() == y.error());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
+ return x.has_value() || (x.error() != y.value());
+}
+template<class T1, class E1, class E2>
+constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
+ return y.has_value() || (x.value() != y.error());
+}
+
+template<class E>
+class _NODISCARD_ expected<void, E> {
+ public:
+ using value_type = void;
+ using error_type = E;
+ using unexpected_type = unexpected<E>;
+
+ // constructors
+ constexpr expected() = default;
+ constexpr expected(const expected& rhs) = default;
+ constexpr expected(expected&& rhs) noexcept = default;
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const expected<U, G>& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(rhs.error());
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ std::is_convertible_v<const G&&, E> /* non-explicit */
+ )>
+ constexpr expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class U, class G _ENABLE_IF(
+ std::is_void_v<U> &&
+ !std::is_convertible_v<const G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(expected<U, G>&& rhs) {
+ if (!rhs.has_value()) var_ = unexpected(std::move(rhs.error()));
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ std::is_convertible_v<const G&, E> /* non-explicit */
+ )>
+ constexpr expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, e.value()) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, const G&> &&
+ !std::is_convertible_v<const G&, E> /* explicit */
+ )>
+ constexpr explicit expected(const unexpected<G>& e)
+ : var_(std::in_place_index<1>, E(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ std::is_convertible_v<G&&, E> /* non-explicit */
+ )>
+ constexpr expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, std::move(e.value())) {}
+
+ template<class G = E _ENABLE_IF(
+ std::is_constructible_v<E, G&&> &&
+ !std::is_convertible_v<G&&, E> /* explicit */
+ )>
+ constexpr explicit expected(unexpected<G>&& e)
+ : var_(std::in_place_index<1>, E(std::move(e.value()))) {}
+
+ template<class... Args _ENABLE_IF(
+ sizeof...(Args) == 0
+ )>
+ constexpr explicit expected(std::in_place_t, Args&&...) {}
+
+ template<class... Args _ENABLE_IF(
+ std::is_constructible_v<E, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, Args&&... args)
+ : var_(unexpected_type(std::forward<Args>(args)...)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
+ : var_(unexpected_type(il, std::forward<Args>(args)...)) {}
+
+ // destructor
+ ~expected() = default;
+
+ // assignment
+ // Note: SFNAIE doesn't work here because assignment operator should be
+ // non-template. We could workaround this by defining a templated parent class
+ // having the assignment operator. This incomplete implementation however
+ // doesn't allow us to copy assign expected<T,E> even when T is non-copy
+ // assignable. The copy assignment will fail by the underlying std::variant
+ // anyway though the error message won't be clear.
+ expected& operator=(const expected& rhs) = default;
+
+ // Note for SFNAIE above applies to here as well
+ expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
+
+ template<class G = E>
+ expected& operator=(const unexpected<G>& rhs) {
+ var_ = rhs;
+ return *this;
+ }
+
+ template<class G = E _ENABLE_IF(
+ std::is_nothrow_move_constructible_v<G> &&
+ std::is_move_assignable_v<G>
+ )>
+ expected& operator=(unexpected<G>&& rhs) {
+ var_ = std::move(rhs);
+ return *this;
+ }
+
+ // modifiers
+ void emplace() {
+ var_ = std::monostate();
+ }
+
+ // swap
+ template<typename = std::enable_if_t<
+ std::is_swappable_v<E>>
+ >
+ void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) {
+ var_.swap(rhs.var_);
+ }
+
+ // observers
+ constexpr explicit operator bool() const noexcept { return has_value(); }
+ constexpr bool has_value() const noexcept { return var_.index() == 0; }
+
+ constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
+
+ constexpr const E& error() const& { return std::get<unexpected_type>(var_).value(); }
+ constexpr E& error() & { return std::get<unexpected_type>(var_).value(); }
+ constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).value(); }
+ constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).value(); }
+
+ // expected equality operators
+ template<class E1, class E2>
+ friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
+
+ // Specialized algorithms
+ template<class T1, class E1>
+ friend void swap(expected<T1, E1>&, expected<T1, E1>&) noexcept;
+
+ private:
+ std::variant<std::monostate, unexpected_type> var_;
+};
+
+template<class E1, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return true;
+ }
+}
+
+template<class T1, class E1, class E2>
+constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return false;
+ }
+}
+
+template<class E1, class T2, class E2>
+constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
+ if (x.has_value() != y.has_value()) {
+ return false;
+ } else if (!x.has_value()) {
+ return x.error() == y.error();
+ } else {
+ return false;
+ }
+}
+
+template<class E>
+class unexpected {
+ public:
+ // constructors
+ constexpr unexpected(const unexpected&) = default;
+ constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
+
+ template <class Err = E _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+ constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
+
+ template<class U, class... Args _ENABLE_IF(
+ std::is_constructible_v<E, std::initializer_list<U>&, Args...>
+ )>
+ constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
+ : val_(il, std::forward<Args>(args)...) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ constexpr unexpected(const unexpected<Err>& rhs)
+ : val_(rhs.value()) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(const unexpected<Err>& rhs)
+ : val_(E(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ std::is_convertible_v<Err, E> /* non-explicit */
+ )>
+ constexpr unexpected(unexpected<Err>&& rhs)
+ : val_(std::move(rhs.value())) {}
+
+ template<class Err _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_constructible_v<E, unexpected<Err>&> &&
+ !std::is_constructible_v<E, unexpected<Err>> &&
+ !std::is_constructible_v<E, const unexpected<Err>&> &&
+ !std::is_constructible_v<E, const unexpected<Err>> &&
+ !std::is_convertible_v<unexpected<Err>&, E> &&
+ !std::is_convertible_v<unexpected<Err>, E> &&
+ !std::is_convertible_v<const unexpected<Err>&, E> &&
+ !std::is_convertible_v<const unexpected<Err>, E> &&
+ !std::is_convertible_v<Err, E> /* explicit */
+ )>
+ constexpr explicit unexpected(unexpected<Err>&& rhs)
+ : val_(E(std::move(rhs.value()))) {}
+
+ // assignment
+ constexpr unexpected& operator=(const unexpected&) = default;
+ constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+ default;
+ template<class Err = E>
+ constexpr unexpected& operator=(const unexpected<Err>& rhs) {
+ val_ = rhs.value();
+ return *this;
+ }
+ template<class Err = E>
+ constexpr unexpected& operator=(unexpected<Err>&& rhs) {
+ val_ = std::forward<E>(rhs.value());
+ return *this;
+ }
+
+ // observer
+ constexpr const E& value() const& noexcept { return val_; }
+ constexpr E& value() & noexcept { return val_; }
+ constexpr const E&& value() const&& noexcept { return std::move(val_); }
+ constexpr E&& value() && noexcept { return std::move(val_); }
+
+ void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
+ std::swap(val_, other.val_);
+ }
+
+ template<class E1, class E2>
+ friend constexpr bool
+ operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
+ template<class E1, class E2>
+ friend constexpr bool
+ operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
+
+ template<class E1>
+ friend void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y)));
+
+ private:
+ E val_;
+};
+
+template<class E1, class E2>
+constexpr bool
+operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() == e2.value();
+}
+
+template<class E1, class E2>
+constexpr bool
+operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
+ return e1.value() != e2.value();
+}
+
+template<class E1>
+void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
+ x.swap(y);
+}
+
+// TODO: bad_expected_access class
+
+#undef _ENABLE_IF
+#undef _NODISCARD_
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 86d537d..c622562 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -18,9 +18,12 @@
#include <sys/stat.h>
#include <sys/types.h>
+
#include <string>
+#include "android-base/macros.h"
#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
#if !defined(_WIN32) && !defined(O_BINARY)
/** Windows needs O_BINARY, but Unix never mangles line endings. */
@@ -32,16 +35,56 @@
#define O_CLOEXEC O_NOINHERIT
#endif
+class TemporaryFile {
+ public:
+ TemporaryFile();
+ explicit TemporaryFile(const std::string& tmp_dir);
+ ~TemporaryFile();
+
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+ // Don't remove the temporary file in the destructor.
+ void DoNotRemove() { remove_file_ = false; }
+
+ int fd;
+ char path[1024];
+
+ private:
+ void init(const std::string& tmp_dir);
+
+ bool remove_file_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+ TemporaryDir();
+ ~TemporaryDir();
+ // Don't remove the temporary dir in the destructor.
+ void DoNotRemove() { remove_dir_and_contents_ = false; }
+
+ char path[1024];
+
+ private:
+ bool init(const std::string& tmp_dir);
+
+ bool remove_dir_and_contents_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
namespace android {
namespace base {
-bool ReadFdToString(int fd, std::string* content);
+bool ReadFdToString(borrowed_fd fd, std::string* content);
bool ReadFileToString(const std::string& path, std::string* content,
bool follow_symlinks = false);
bool WriteStringToFile(const std::string& content, const std::string& path,
bool follow_symlinks = false);
-bool WriteStringToFd(const std::string& content, int fd);
+bool WriteStringToFd(const std::string& content, borrowed_fd fd);
#if !defined(_WIN32)
bool WriteStringToFile(const std::string& content, const std::string& path,
@@ -49,7 +92,7 @@
bool follow_symlinks = false);
#endif
-bool ReadFully(int fd, void* data, size_t byte_count);
+bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
// Reads `byte_count` bytes from the file descriptor at the specified offset.
// Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
@@ -59,9 +102,9 @@
// get modified. This means that ReadFullyAtOffset can be used concurrently with other calls to the
// same function, but concurrently seeking or reading incrementally can lead to unexpected
// behavior.
-bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
+bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
-bool WriteFully(int fd, const void* data, size_t byte_count);
+bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/base/include/android-base/format.h
similarity index 62%
copy from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
copy to base/include/android-base/format.h
index 5d0d924..6799c1f 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/base/include/android-base/format.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+#pragma once
-#include <stdint.h>
-#include <string>
-
-namespace unwindstack {
-class Memory;
-}
-
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
+// We include fmtlib here as an alias, since libbase will have fmtlib statically linked already.
+// It is accessed through its normal fmt:: namespace.
+#include <fmt/core.h>
+#include <fmt/format.h>
+#include <fmt/ostream.h>
+#include <fmt/printf.h>
+#include <fmt/time.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index f94cc25..ab6476c 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -469,7 +469,7 @@
} // namespace base
} // namespace android
-namespace std {
+namespace std { // NOLINT(cert-dcl58-cpp)
// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
//
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 80513b1..2ab49ab 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -16,13 +16,14 @@
#pragma once
-#include "android-base/macros.h"
-#include "android-base/off64_t.h"
-
#include <sys/types.h>
#include <memory>
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+#include "android-base/unique_fd.h"
+
#if defined(_WIN32)
#include <windows.h>
#define PROT_READ 1
@@ -35,7 +36,7 @@
namespace base {
/**
- * A region of a file mapped into memory.
+ * A region of a file mapped into memory, also known as MmapFile.
*/
class MappedFile {
public:
@@ -44,7 +45,8 @@
* `offset` does not need to be page-aligned. If `PROT_WRITE` is set in `prot`, the mapping
* will be writable, otherwise it will be read-only. Mappings are always `MAP_SHARED`.
*/
- static std::unique_ptr<MappedFile> FromFd(int fd, off64_t offset, size_t length, int prot);
+ static std::unique_ptr<MappedFile> FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot);
/**
* Removes the mapping.
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
new file mode 100644
index 0000000..69ed3fb
--- /dev/null
+++ b/base/include/android-base/process.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace base {
+
+class AllPids {
+ class PidIterator {
+ public:
+ PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
+ PidIterator& operator++() {
+ Increment();
+ return *this;
+ }
+ bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
+ bool operator!=(const PidIterator& other) const { return !(*this == other); }
+ long operator*() const { return pid_; }
+ // iterator traits
+ using difference_type = pid_t;
+ using value_type = pid_t;
+ using pointer = const pid_t*;
+ using reference = const pid_t&;
+ using iterator_category = std::input_iterator_tag;
+
+ private:
+ void Increment();
+
+ pid_t pid_ = -1;
+ std::unique_ptr<DIR, decltype(&closedir)> dir_;
+ };
+
+ public:
+ PidIterator begin() { return opendir("/proc"); }
+ PidIterator end() { return nullptr; }
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 31e5273..31823df 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -49,9 +49,6 @@
T max = std::numeric_limits<T>::max());
// Sets the system property `key` to `value`.
-// Note that system property setting is inherently asynchronous so a return value of `true`
-// isn't particularly meaningful, and immediately reading back the value won't necessarily
-// tell you whether or not your call succeeded. A `false` return value definitely means failure.
bool SetProperty(const std::string& key, const std::string& value);
// Waits for the system property `key` to have the value `expected_value`.
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
new file mode 100644
index 0000000..1b763af
--- /dev/null
+++ b/base/include/android-base/result.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file contains classes for returning a successful result along with an optional
+// arbitrarily typed return value or for returning a failure result along with an optional string
+// indicating why the function failed.
+
+// There are 3 classes that implement this functionality and one additional helper type.
+//
+// Result<T> either contains a member of type T that can be accessed using similar semantics as
+// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
+// Result<T>::error().
+//
+// ResultError is a type that contains both a std::string describing the error and a copy of errno
+// from when the error occurred. ResultError can be used in an ostream directly to print its
+// string value.
+//
+// Result<void> is the correct return type for a function that either returns successfully or
+// returns an error value. Returning {} from a function that returns Result<void> is the
+// correct way to indicate that a function without a return type has completed successfully.
+//
+// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
+// to T or from the constructor arguments for T. This allows you to return a type T directly from
+// a function that returns Result<T>.
+//
+// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
+// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
+// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
+// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
+// value can be directly specified via the Error() constructor.
+//
+// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
+// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
+//
+// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
+// In this case, the string that the ResultError takes is passed through the stream normally, but
+// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
+// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
+// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
+// used, the errno of the last one is respected.
+//
+// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
+// function that return Result<T> but you have a Result<U> and want to return its error. In this
+// case, you can return the .error() from the Result<U> to construct the Result<T>.
+
+// An example of how to use these is below:
+// Result<U> CalculateResult(const T& input) {
+// U output;
+// if (!SomeOtherCppFunction(input, &output)) {
+// return Errorf("SomeOtherCppFunction {} failed", input);
+// }
+// if (!c_api_function(output)) {
+// return ErrnoErrorf("c_api_function {} failed", output);
+// }
+// return output;
+// }
+//
+// auto output = CalculateResult(input);
+// if (!output) return Error() << "CalculateResult failed: " << output.error();
+// UseOutput(*output);
+
+#pragma once
+
+#include <errno.h>
+
+#include <sstream>
+#include <string>
+
+#include "android-base/expected.h"
+#include "android-base/format.h"
+
+namespace android {
+namespace base {
+
+struct ResultError {
+ template <typename T>
+ ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
+
+ template <typename T>
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(message_, code_));
+ }
+
+ std::string message() const { return message_; }
+ int code() const { return code_; }
+
+ private:
+ std::string message_;
+ int code_;
+};
+
+inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
+ return lhs.message() == rhs.message() && lhs.code() == rhs.code();
+}
+
+inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+ os << t.message();
+ return os;
+}
+
+class Error {
+ public:
+ Error() : errno_(0), append_errno_(false) {}
+ Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+
+ template <typename T>
+ operator android::base::expected<T, ResultError>() {
+ return android::base::unexpected(ResultError(str(), errno_));
+ }
+
+ template <typename T>
+ Error& operator<<(T&& t) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ errno_ = t.code();
+ return (*this) << t.message();
+ }
+ int saved = errno;
+ ss_ << t;
+ errno = saved;
+ return *this;
+ }
+
+ const std::string str() const {
+ std::string str = ss_.str();
+ if (append_errno_) {
+ if (str.empty()) {
+ return strerror(errno_);
+ }
+ return std::move(str) + ": " + strerror(errno_);
+ }
+ return str;
+ }
+
+ Error(const Error&) = delete;
+ Error(Error&&) = delete;
+ Error& operator=(const Error&) = delete;
+ Error& operator=(Error&&) = delete;
+
+ template <typename... Args>
+ friend Error Errorf(const char* fmt, const Args&... args);
+
+ template <typename... Args>
+ friend Error ErrnoErrorf(const char* fmt, const Args&... args);
+
+ private:
+ Error(bool append_errno, int errno_to_append, const std::string& message)
+ : errno_(errno_to_append), append_errno_(append_errno) {
+ (*this) << message;
+ }
+
+ std::stringstream ss_;
+ int errno_;
+ const bool append_errno_;
+};
+
+inline Error ErrnoError() {
+ return Error(errno);
+}
+
+inline int ErrorCode(int code) {
+ return code;
+}
+
+// Return the error code of the last ResultError object, if any.
+// Otherwise, return `code` as it is.
+template <typename T, typename... Args>
+inline int ErrorCode(int code, T&& t, const Args&... args) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+ return ErrorCode(t.code(), args...);
+ }
+ return ErrorCode(code, args...);
+}
+
+template <typename... Args>
+inline Error Errorf(const char* fmt, const Args&... args) {
+ return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
+}
+
+template <typename... Args>
+inline Error ErrnoErrorf(const char* fmt, const Args&... args) {
+ return Error(true, errno, fmt::format(fmt, args...));
+}
+
+template <typename T>
+using Result = android::base::expected<T, ResultError>;
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index 9c35560..b1c22c9 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
+#include <string_view>
#include <vector>
namespace android {
@@ -56,21 +57,33 @@
extern template std::string Join(const std::vector<const char*>&, const std::string&);
// Tests whether 's' starts with 'prefix'.
-// TODO: string_view
-bool StartsWith(const std::string& s, const char* prefix);
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix);
-bool StartsWith(const std::string& s, const std::string& prefix);
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool StartsWith(std::string_view s, std::string_view prefix);
+bool StartsWith(std::string_view s, char prefix);
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix);
// Tests whether 's' ends with 'suffix'.
-// TODO: string_view
-bool EndsWith(const std::string& s, const char* suffix);
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& suffix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
+bool EndsWith(std::string_view s, std::string_view suffix);
+bool EndsWith(std::string_view s, char suffix);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix);
// Tests whether 'lhs' equals 'rhs', ignoring case.
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs);
+
+// Removes `prefix` from the start of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumePrefix(std::string_view* s, std::string_view prefix) {
+ if (!StartsWith(*s, prefix)) return false;
+ s->remove_prefix(prefix.size());
+ return true;
+}
+
+// Removes `suffix` from the end of the given string and returns true (if
+// it was present), false otherwise.
+inline bool ConsumeSuffix(std::string_view* s, std::string_view suffix) {
+ if (!EndsWith(*s, suffix)) return false;
+ s->remove_suffix(suffix.size());
+ return true;
+}
} // namespace base
} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 2abe68e..b20f278 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -19,44 +19,9 @@
#include <regex>
#include <string>
+#include <android-base/file.h>
#include <android-base/macros.h>
-class TemporaryFile {
- public:
- TemporaryFile();
- explicit TemporaryFile(const std::string& tmp_dir);
- ~TemporaryFile();
-
- // Release the ownership of fd, caller is reponsible for closing the
- // fd or stream properly.
- int release();
- // Don't remove the temporary file in the destructor.
- void DoNotRemove() { remove_file_ = false; }
-
- int fd;
- char path[1024];
-
- private:
- void init(const std::string& tmp_dir);
-
- bool remove_file_ = true;
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
-};
-
-class TemporaryDir {
- public:
- TemporaryDir();
- ~TemporaryDir();
-
- char path[1024];
-
- private:
- bool init(const std::string& tmp_dir);
-
- DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
-};
-
class CapturedStdFd {
public:
CapturedStdFd(int std_fd);
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 5c55e63..53fe6da 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -16,6 +16,8 @@
#pragma once
+#include <mutex>
+
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#define CAPABILITY(x) \
@@ -104,3 +106,39 @@
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+namespace android {
+namespace base {
+
+// A class to help thread safety analysis deal with std::unique_lock and condition_variable.
+//
+// Clang's thread safety analysis currently doesn't perform alias analysis, so movable types
+// like std::unique_lock can't be marked with thread safety annotations. This helper allows
+// for manual assertion of lock state in a scope.
+//
+// For example:
+//
+// std::mutex mutex;
+// std::condition_variable cv;
+// std::vector<int> vec GUARDED_BY(mutex);
+//
+// int pop() {
+// std::unique_lock lock(mutex);
+// ScopedLockAssertion lock_assertion(mutex);
+// cv.wait(lock, []() {
+// ScopedLockAssertion lock_assertion(mutex);
+// return !vec.empty();
+// });
+//
+// int result = vec.back();
+// vec.pop_back();
+// return result;
+// }
+class SCOPED_CAPABILITY ScopedLockAssertion {
+ public:
+ ScopedLockAssertion(std::mutex& mutex) ACQUIRE(mutex) {}
+ ~ScopedLockAssertion() RELEASE() {}
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index cd2dc04..6e11b4e 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -16,6 +16,8 @@
#pragma once
+#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#if !defined(_WIN32)
@@ -90,6 +92,8 @@
explicit unique_fd_impl(int fd) { reset(fd); }
~unique_fd_impl() { reset(); }
+ unique_fd_impl(const unique_fd_impl&) = delete;
+ void operator=(const unique_fd_impl&) = delete;
unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
int fd = s.fd_;
@@ -101,7 +105,22 @@
void reset(int new_value = -1) { reset(new_value, nullptr); }
int get() const { return fd_; }
- operator int() const { return get(); }
+
+#if !defined(ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION)
+ // unique_fd's operator int is dangerous, but we have way too much code that
+ // depends on it, so make this opt-in at first.
+ operator int() const { return get(); } // NOLINT
+#endif
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
+
+ // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
+ bool operator!() const = delete;
+
+ bool ok() const { return get() != -1; }
int release() __attribute__((warn_unused_result)) {
tag(fd_, this, nullptr);
@@ -112,6 +131,8 @@
private:
void reset(int new_value, void* previous_tag) {
+ int previous_errno = errno;
+
if (fd_ != -1) {
close(fd_, this);
}
@@ -120,6 +141,8 @@
if (new_value != -1) {
tag(new_value, previous_tag, this);
}
+
+ errno = previous_errno;
}
int fd_ = -1;
@@ -148,9 +171,6 @@
static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
T::Close(fd);
}
-
- unique_fd_impl(const unique_fd_impl&);
- void operator=(const unique_fd_impl&);
};
using unique_fd = unique_fd_impl<DefaultCloser>;
@@ -159,22 +179,35 @@
// Inline functions, so that they can be used header-only.
template <typename Closer>
-inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write) {
+inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
+ int flags = O_CLOEXEC) {
int pipefd[2];
#if defined(__linux__)
- if (pipe2(pipefd, O_CLOEXEC) != 0) {
+ if (pipe2(pipefd, flags) != 0) {
return false;
}
#else // defined(__APPLE__)
+ if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
+ return false;
+ }
if (pipe(pipefd) != 0) {
return false;
}
- if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
- close(pipefd[0]);
- close(pipefd[1]);
- return false;
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
+ }
+ if (flags & O_NONBLOCK) {
+ if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0 || fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ }
}
#endif
@@ -211,11 +244,48 @@
return file;
}
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+ int fd = ufd.release();
+ DIR* dir = fdopendir(fd);
+ if (dir == nullptr) {
+ close(fd);
+ }
+ return dir;
+}
+
#endif // !defined(_WIN32)
+// A wrapper type that can be implicitly constructed from either int or unique_fd.
+struct borrowed_fd {
+ /* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
+ template <typename T>
+ /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {} // NOLINT
+
+ int get() const { return fd_; }
+
+ bool operator>=(int rhs) const { return get() >= rhs; }
+ bool operator<(int rhs) const { return get() < rhs; }
+ bool operator==(int rhs) const { return get() == rhs; }
+ bool operator!=(int rhs) const { return get() != rhs; }
+
+ private:
+ int fd_ = -1;
+};
} // namespace base
} // namespace android
template <typename T>
int close(const android::base::unique_fd_impl<T>&)
__attribute__((__unavailable__("close called on unique_fd")));
+
+template <typename T>
+FILE* fdopen(const android::base::unique_fd_impl<T>&, const char* mode)
+ __attribute__((__unavailable__("fdopen takes ownership of the fd passed in; either dup the "
+ "unique_fd, or use android::base::Fdopen to pass ownership")));
+
+template <typename T>
+DIR* fdopendir(const android::base::unique_fd_impl<T>&) __attribute__((
+ __unavailable__("fdopendir takes ownership of the fd passed in; either dup the "
+ "unique_fd, or use android::base::Fdopendir to pass ownership")));
diff --git a/base/logging.cpp b/base/logging.cpp
index bd09069..f89168c 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -417,6 +417,14 @@
}
std::string msg(data_->ToString());
+ if (data_->GetSeverity() == FATAL) {
+#ifdef __ANDROID__
+ // Set the bionic abort message early to avoid liblog doing it
+ // with the individual lines, so that we get the whole message.
+ android_set_abort_message(msg.c_str());
+#endif
+ }
+
{
// Do the actual logging with the lock held.
std::lock_guard<std::mutex> lock(LoggingLock());
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index f7901af..f60de56 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,6 +16,10 @@
#include "android-base/mapped_file.h"
+#include <errno.h>
+
+#include "android-base/unique_fd.h"
+
namespace android {
namespace base {
@@ -29,7 +33,8 @@
#endif
}
-std::unique_ptr<MappedFile> MappedFile::FromFd(int fd, off64_t offset, size_t length, int prot) {
+std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
+ int prot) {
static off64_t page_size = InitPageSize();
size_t slop = offset % page_size;
off64_t file_offset = offset - slop;
@@ -37,9 +42,16 @@
#if defined(_WIN32)
HANDLE handle =
- CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), nullptr,
+ CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
(prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
- if (handle == nullptr) return nullptr;
+ if (handle == nullptr) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // Return a MappedFile that's only valid for reading the size.
+ if (length == 0) {
+ return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+ }
+ return nullptr;
+ }
void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
file_offset, file_length);
if (base == nullptr) {
@@ -49,8 +61,15 @@
return std::unique_ptr<MappedFile>(
new MappedFile{static_cast<char*>(base), length, slop, handle});
#else
- void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, file_offset);
- if (base == MAP_FAILED) return nullptr;
+ void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
+ if (base == MAP_FAILED) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // mmap fails with EINVAL for a zero length region.
+ if (errno == EINVAL && length == 0) {
+ return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+ }
+ return nullptr;
+ }
return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
#endif
}
@@ -60,7 +79,7 @@
if (base_ != nullptr) UnmapViewOfFile(base_);
if (handle_ != nullptr) CloseHandle(handle_);
#else
- if (base_ != nullptr) munmap(base_, size_);
+ if (base_ != nullptr) munmap(base_, size_ + offset_);
#endif
base_ = nullptr;
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index 57fde6f..cfde73c 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -25,8 +25,6 @@
#include <string>
#include "android-base/file.h"
-#include "android-base/test_utils.h"
-#include "android-base/unique_fd.h"
TEST(mapped_file, smoke) {
TemporaryFile tf;
@@ -38,3 +36,13 @@
ASSERT_EQ('l', m->data()[0]);
ASSERT_EQ('o', m->data()[1]);
}
+
+TEST(mapped_file, zero_length_mapping) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // mmap fails with EINVAL for a zero length region.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
+ ASSERT_EQ(0u, m->size());
+}
diff --git a/base/process.cpp b/base/process.cpp
new file mode 100644
index 0000000..b8cabf6
--- /dev/null
+++ b/base/process.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/process.h"
+
+namespace android {
+namespace base {
+
+void AllPids::PidIterator::Increment() {
+ if (!dir_) {
+ return;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir_.get())) != nullptr) {
+ pid_t pid = atoi(de->d_name);
+ if (pid != 0) {
+ pid_ = pid;
+ return;
+ }
+ }
+ pid_ = -1;
+}
+
+} // namespace base
+} // namespace android
diff --git a/libutils/tests/Mutex_test.cpp b/base/process_test.cpp
similarity index 62%
copy from libutils/tests/Mutex_test.cpp
copy to base/process_test.cpp
index 8a1805f..056f667 100644
--- a/libutils/tests/Mutex_test.cpp
+++ b/base/process_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-#include <utils/Mutex.h>
+#include "android-base/process.h"
+
+#include <unistd.h>
#include <gtest/gtest.h>
-static android::Mutex mLock;
-static int i GUARDED_BY(mLock);
+TEST(process, find_ourselves) {
+#if defined(__linux__)
+ bool found_our_pid = false;
+ for (const auto& pid : android::base::AllPids{}) {
+ if (pid == getpid()) {
+ found_our_pid = true;
+ }
+ }
-void modifyLockedVariable() REQUIRES(mLock) {
- i = 1;
+ EXPECT_TRUE(found_our_pid);
+
+#endif
}
-
-TEST(Mutex, compile) {
- android::Mutex::Autolock _l(mLock);
- i = 0;
- modifyLockedVariable();
-}
\ No newline at end of file
diff --git a/base/result_test.cpp b/base/result_test.cpp
new file mode 100644
index 0000000..2ee4057
--- /dev/null
+++ b/base/result_test.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/result.h"
+
+#include "errno.h"
+
+#include <istream>
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace base {
+
+TEST(result, result_accessors) {
+ Result<std::string> result = "success";
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("success", *result);
+ EXPECT_EQ("success", result.value());
+
+ EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+ ASSERT_TRUE(Result<std::string>("success"));
+ ASSERT_TRUE(Result<std::string>("success").has_value());
+
+ EXPECT_EQ("success", *Result<std::string>("success"));
+ EXPECT_EQ("success", Result<std::string>("success").value());
+
+ EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_void) {
+ Result<void> ok = {};
+ EXPECT_TRUE(ok);
+ ok.value(); // should not crash
+ ASSERT_DEATH(ok.error(), "");
+
+ Result<void> fail = Error() << "failure" << 1;
+ EXPECT_FALSE(fail);
+ EXPECT_EQ("failure1", fail.error().message());
+ EXPECT_EQ(0, fail.error().code());
+ EXPECT_TRUE(ok != fail);
+ ASSERT_DEATH(fail.value(), "");
+
+ auto test = [](bool ok) -> Result<void> {
+ if (ok) return {};
+ else return Error() << "failure" << 1;
+ };
+ EXPECT_TRUE(test(true));
+ EXPECT_FALSE(test(false));
+ test(true).value(); // should not crash
+ ASSERT_DEATH(test(true).error(), "");
+ ASSERT_DEATH(test(false).value(), "");
+ EXPECT_EQ("failure1", test(false).error().message());
+}
+
+TEST(result, result_error) {
+ Result<void> result = Error() << "failure" << 1;
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("failure1", result.error().message());
+}
+
+TEST(result, result_error_empty) {
+ Result<void> result = Error();
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(0, result.error().code());
+ EXPECT_EQ("", result.error().message());
+}
+
+TEST(result, result_error_rvalue) {
+ // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+ // Under the hood, they are an intermediate class that can be implicitly constructed into a
+ // Result<T>. This is needed both to create the ostream and because Error() itself, by
+ // definition will not know what the type, T, of the underlying Result<T> object that it would
+ // create is.
+
+ auto MakeRvalueErrorResult = []() -> Result<void> { return Error() << "failure" << 1; };
+ ASSERT_FALSE(MakeRvalueErrorResult());
+ ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+ EXPECT_EQ(0, MakeRvalueErrorResult().error().code());
+ EXPECT_EQ("failure1", MakeRvalueErrorResult().error().message());
+}
+
+TEST(result, result_errno_error) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError() << "failure" << 1;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("failure1: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_errno_error_no_text) {
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ Result<void> result = ErrnoError();
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ(strerror(test_errno), result.error().message());
+}
+
+TEST(result, result_error_from_other_result) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_error_through_ostream) {
+ auto error_text = "test error"s;
+ Result<void> result = Error() << error_text;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(0, result2.error().code());
+ EXPECT_EQ(error_text, result2.error().message());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+ auto error_text = "test error"s;
+ constexpr int test_errno = 6;
+ errno = 6;
+ Result<void> result = ErrnoError() << error_text;
+
+ errno = 0;
+
+ ASSERT_FALSE(result);
+ ASSERT_FALSE(result.has_value());
+
+ Result<std::string> result2 = Error() << result.error();
+
+ ASSERT_FALSE(result2);
+ ASSERT_FALSE(result2.has_value());
+
+ EXPECT_EQ(test_errno, result2.error().code());
+ EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
+}
+
+TEST(result, constructor_forwarding) {
+ auto result = Result<std::string>(std::in_place, 5, 'a');
+
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.has_value());
+
+ EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+ static size_t constructor_called;
+ static size_t copy_constructor_called;
+ static size_t move_constructor_called;
+ static size_t copy_assignment_called;
+ static size_t move_assignment_called;
+
+ template <typename T>
+ ConstructorTracker(T&& string) : string(string) {
+ ++constructor_called;
+ }
+
+ ConstructorTracker(const ConstructorTracker& ct) {
+ ++copy_constructor_called;
+ string = ct.string;
+ }
+ ConstructorTracker(ConstructorTracker&& ct) noexcept {
+ ++move_constructor_called;
+ string = std::move(ct.string);
+ }
+ ConstructorTracker& operator=(const ConstructorTracker& ct) {
+ ++copy_assignment_called;
+ string = ct.string;
+ return *this;
+ }
+ ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+ ++move_assignment_called;
+ string = std::move(ct.string);
+ return *this;
+ }
+
+ std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+ if (in.empty()) {
+ return "literal string";
+ }
+ if (in == "test2") {
+ return ConstructorTracker(in + in + "2");
+ }
+ ConstructorTracker result(in + " " + in);
+ return result;
+};
+
+TEST(result, no_copy_on_return) {
+ // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+ // then those parameters are forwarded to the construction of Result<T>.
+
+ // If returning an prvalue or xvalue, it will be move constructed during the construction of
+ // Result<T>.
+
+ // This check ensures that that is the case, and particularly that no copy constructors
+ // are called.
+
+ auto result1 = ReturnConstructorTracker("");
+ ASSERT_TRUE(result1);
+ EXPECT_EQ("literal string", result1->string);
+ EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result2 = ReturnConstructorTracker("test2");
+ ASSERT_TRUE(result2);
+ EXPECT_EQ("test2test22", result2->string);
+ EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+ auto result3 = ReturnConstructorTracker("test3");
+ ASSERT_TRUE(result3);
+ EXPECT_EQ("test3 test3", result3->string);
+ EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+ EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+ EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+ EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor. This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+ auto return_result_result_with_success = []() -> Result<Result<void>> { return Result<void>(); };
+ auto result = return_result_result_with_success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(*result);
+
+ auto inner_result = result.value();
+ ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+ auto return_result_result_with_error = []() -> Result<Result<void>> {
+ return Result<void>(ResultError("failure string", 6));
+ };
+ auto result = return_result_result_with_error();
+ ASSERT_TRUE(result);
+ ASSERT_FALSE(*result);
+ EXPECT_EQ("failure string", (*result).error().message());
+ EXPECT_EQ(6, (*result).error().code());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+ struct TestStruct {
+ TestStruct(int value) : value_(value) {}
+ TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+ int value_;
+ };
+
+ auto return_test_struct = []() -> Result<TestStruct> {
+ return Result<TestStruct>(std::in_place, Result<TestStruct>(std::in_place, 6), 6);
+ };
+
+ auto result = return_test_struct();
+ ASSERT_TRUE(result);
+ EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+ Result<std::string> result = Error();
+ ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+ Result<std::string> result = "success";
+ ASSERT_DEATH(result.error(), "");
+}
+
+template <class CharT>
+std::basic_ostream<CharT>& SetErrnoToTwo(std::basic_ostream<CharT>& ss) {
+ errno = 2;
+ return ss;
+}
+
+TEST(result, preserve_errno) {
+ errno = 1;
+ int old_errno = errno;
+ Result<int> result = Error() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result);
+ EXPECT_EQ(old_errno, errno);
+
+ errno = 1;
+ old_errno = errno;
+ Result<int> result2 = ErrnoError() << "Failed" << SetErrnoToTwo<char>;
+ ASSERT_FALSE(result2);
+ EXPECT_EQ(old_errno, errno);
+ EXPECT_EQ(old_errno, result2.error().code());
+}
+
+TEST(result, error_with_fmt) {
+ Result<int> result = Errorf("{} {}!", "hello", "world");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{} {}!", std::string("hello"), std::string("world"));
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("{h} {w}!", fmt::arg("w", "world"), fmt::arg("h", "hello"));
+ EXPECT_EQ("hello world!", result.error().message());
+
+ result = Errorf("hello world!");
+ EXPECT_EQ("hello world!", result.error().message());
+
+ Result<int> result2 = Errorf("error occurred with {}", result.error());
+ EXPECT_EQ("error occurred with hello world!", result2.error().message());
+
+ constexpr int test_errno = 6;
+ errno = test_errno;
+ result = ErrnoErrorf("{} {}!", "hello", "world");
+ EXPECT_EQ(test_errno, result.error().code());
+ EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
+}
+
+TEST(result, error_with_fmt_carries_errno) {
+ constexpr int inner_errno = 6;
+ errno = inner_errno;
+ Result<int> inner_result = ErrnoErrorf("inner failure");
+ errno = 0;
+ EXPECT_EQ(inner_errno, inner_result.error().code());
+
+ // outer_result is created with Errorf, but its error code is got from inner_result.
+ Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(inner_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
+ outer_result.error().message());
+
+ // now both result objects are created with ErrnoErrorf. errno from the inner_result
+ // is not passed to outer_result.
+ constexpr int outer_errno = 10;
+ errno = outer_errno;
+ outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
+ EXPECT_EQ(outer_errno, outer_result.error().code());
+ EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
+ strerror(outer_errno),
+ outer_result.error().message());
+}
+
+TEST(result, errno_chaining_multiple) {
+ constexpr int errno1 = 6;
+ errno = errno1;
+ Result<int> inner1 = ErrnoErrorf("error1");
+
+ constexpr int errno2 = 10;
+ errno = errno2;
+ Result<int> inner2 = ErrnoErrorf("error2");
+
+ // takes the error code of inner2 since its the last one.
+ Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
+ EXPECT_EQ(errno2, outer.error().code());
+ EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
+ outer.error().message());
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/strings.cpp b/base/strings.cpp
index a8bb2a9..bb3167e 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -87,50 +87,33 @@
template std::string Join(const std::vector<std::string>&, const std::string&);
template std::string Join(const std::vector<const char*>&, const std::string&);
-bool StartsWith(const std::string& s, const char* prefix) {
- return strncmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWith(std::string_view s, std::string_view prefix) {
+ return s.substr(0, prefix.size()) == prefix;
}
-bool StartsWith(const std::string& s, const std::string& prefix) {
- return strncmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool StartsWith(std::string_view s, char prefix) {
+ return !s.empty() && s.front() == prefix;
}
-bool StartsWithIgnoreCase(const std::string& s, const char* prefix) {
- return strncasecmp(s.c_str(), prefix, strlen(prefix)) == 0;
+bool StartsWithIgnoreCase(std::string_view s, std::string_view prefix) {
+ return s.size() >= prefix.size() && strncasecmp(s.data(), prefix.data(), prefix.size()) == 0;
}
-bool StartsWithIgnoreCase(const std::string& s, const std::string& prefix) {
- return strncasecmp(s.c_str(), prefix.c_str(), prefix.size()) == 0;
+bool EndsWith(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() && s.substr(s.size() - suffix.size(), suffix.size()) == suffix;
}
-static bool EndsWith(const std::string& s, const char* suffix, size_t suffix_length,
- bool case_sensitive) {
- size_t string_length = s.size();
- if (suffix_length > string_length) {
- return false;
- }
- size_t offset = string_length - suffix_length;
- return (case_sensitive ? strncmp : strncasecmp)(s.c_str() + offset, suffix, suffix_length) == 0;
+bool EndsWith(std::string_view s, char suffix) {
+ return !s.empty() && s.back() == suffix;
}
-bool EndsWith(const std::string& s, const char* suffix) {
- return EndsWith(s, suffix, strlen(suffix), true);
+bool EndsWithIgnoreCase(std::string_view s, std::string_view suffix) {
+ return s.size() >= suffix.size() &&
+ strncasecmp(s.data() + (s.size() - suffix.size()), suffix.data(), suffix.size()) == 0;
}
-bool EndsWith(const std::string& s, const std::string& suffix) {
- return EndsWith(s, suffix.c_str(), suffix.size(), true);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const char* suffix) {
- return EndsWith(s, suffix, strlen(suffix), false);
-}
-
-bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix) {
- return EndsWith(s, suffix.c_str(), suffix.size(), false);
-}
-
-bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs) {
- return strcasecmp(lhs.c_str(), rhs.c_str()) == 0;
+bool EqualsIgnoreCase(std::string_view lhs, std::string_view rhs) {
+ return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
} // namespace base
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index b8639ea..ca3c0b8 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -198,6 +198,12 @@
ASSERT_FALSE(android::base::StartsWithIgnoreCase("foobar", "BAR"));
}
+TEST(strings, StartsWith_char) {
+ ASSERT_FALSE(android::base::StartsWith("", 'f'));
+ ASSERT_TRUE(android::base::StartsWith("foo", 'f'));
+ ASSERT_FALSE(android::base::StartsWith("foo", 'o'));
+}
+
TEST(strings, EndsWith_empty) {
ASSERT_FALSE(android::base::EndsWith("", "foo"));
ASSERT_TRUE(android::base::EndsWith("", ""));
@@ -273,6 +279,12 @@
ASSERT_FALSE(android::base::EndsWithIgnoreCase("GoOdByE", std::string{"lo"}));
}
+TEST(strings, EndsWith_char) {
+ ASSERT_FALSE(android::base::EndsWith("", 'o'));
+ ASSERT_TRUE(android::base::EndsWith("foo", 'o'));
+ ASSERT_FALSE(android::base::EndsWith("foo", "f"));
+}
+
TEST(strings, EqualsIgnoreCase) {
ASSERT_TRUE(android::base::EqualsIgnoreCase("foo", "FOO"));
ASSERT_TRUE(android::base::EqualsIgnoreCase("FOO", "foo"));
@@ -283,3 +295,19 @@
TEST(strings, ubsan_28729303) {
android::base::Split("/dev/null", ":");
}
+
+TEST(strings, ConsumePrefix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumePrefix(&s, "bar."));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumePrefix(&s, "foo."));
+ ASSERT_EQ("bar", s);
+}
+
+TEST(strings, ConsumeSuffix) {
+ std::string_view s{"foo.bar"};
+ ASSERT_FALSE(android::base::ConsumeSuffix(&s, ".foo"));
+ ASSERT_EQ("foo.bar", s);
+ ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
+ ASSERT_EQ("foo", s);
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 4d9466b..36b4cdf 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -22,109 +22,11 @@
#include <sys/stat.h>
#include <unistd.h>
-#if defined(_WIN32)
-#include <windows.h>
-#include <direct.h>
-#define OS_PATH_SEPARATOR '\\'
-#else
-#define OS_PATH_SEPARATOR '/'
-#endif
-
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
-#ifdef _WIN32
-int mkstemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
- return -1;
- }
- // Use open() to match the close() that TemporaryFile's destructor does.
- // Use O_BINARY to match base file APIs.
- return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
- S_IRUSR | S_IWUSR);
-}
-
-char* mkdtemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
- return nullptr;
- }
- if (_mkdir(template_name) == -1) {
- return nullptr;
- }
- return template_name;
-}
-#endif
-
-static std::string GetSystemTempDir() {
-#if defined(__ANDROID__)
- const char* tmpdir = "/data/local/tmp";
- if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
- return tmpdir;
- }
- // Tests running in app context can't access /data/local/tmp,
- // so try current directory if /data/local/tmp is not accessible.
- return ".";
-#elif defined(_WIN32)
- char tmp_dir[MAX_PATH];
- DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
- CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
- CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
-
- // GetTempPath() returns a path with a trailing slash, but init()
- // does not expect that, so remove it.
- CHECK_EQ(tmp_dir[result - 1], '\\');
- tmp_dir[result - 1] = '\0';
- return tmp_dir;
-#else
- return "/tmp";
-#endif
-}
-
-TemporaryFile::TemporaryFile() {
- init(GetSystemTempDir());
-}
-
-TemporaryFile::TemporaryFile(const std::string& tmp_dir) {
- init(tmp_dir);
-}
-
-TemporaryFile::~TemporaryFile() {
- if (fd != -1) {
- close(fd);
- }
- if (remove_file_) {
- unlink(path);
- }
-}
-
-int TemporaryFile::release() {
- int result = fd;
- fd = -1;
- return result;
-}
-
-void TemporaryFile::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
- OS_PATH_SEPARATOR);
- fd = mkstemp(path);
-}
-
-TemporaryDir::TemporaryDir() {
- init(GetSystemTempDir());
-}
-
-TemporaryDir::~TemporaryDir() {
- rmdir(path);
-}
-
-bool TemporaryDir::init(const std::string& tmp_dir) {
- snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
- OS_PATH_SEPARATOR);
- return (mkdtemp(path) != nullptr);
-}
-
CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
Start();
}
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
index fcb25c3..472e82c 100644
--- a/base/utf8_test.cpp
+++ b/base/utf8_test.cpp
@@ -21,8 +21,8 @@
#include <fcntl.h>
#include <stdlib.h>
+#include "android-base/file.h"
#include "android-base/macros.h"
-#include "android-base/test_utils.h"
#include "android-base/unique_fd.h"
namespace android {
diff --git a/bootstat/OWNERS b/bootstat/OWNERS
index 7fe0443..50b2097 100644
--- a/bootstat/OWNERS
+++ b/bootstat/OWNERS
@@ -1 +1,2 @@
jhawkins@google.com
+salyzyn@google.com
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 4b7ab36..5ca9b09 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -29,7 +29,6 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 71d3ecb..8979b0c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -25,6 +25,8 @@
# Best guess to an average device's reboot time, refined as tests return
DURATION_DEFAULT=45
STOP_ON_FAILURE=false
+progname="${0##*/}"
+progpath="${0%${progname}}"
# Helper functions
@@ -42,11 +44,40 @@
adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
}
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command succeeded" ]
+adb_sh() {
+ local args=
+ for i in "${@}"; do
+ [ -z "${args}" ] || args="${args} "
+ if [ X"${i}" != X"${i#\'}" ]; then
+ args="${args}${i}"
+ elif [ X"${i}" != X"${i#*\\}" ]; then
+ args="${args}`echo ${i} | sed 's/\\\\/\\\\\\\\/g'`"
+ elif [ X"${i}" != X"${i#* }" ]; then
+ args="${args}'${i}'"
+ elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+ args="${args}'${i}'"
+ else
+ args="${args}${i}"
+ fi
+ done
+ adb shell "${args}"
+}
+
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
+
+Returns: true if the command running as root succeeded" ]
+adb_su() {
+ adb_sh su root "${@}"
+}
+
[ "USAGE: hasPstore
Returns: true if device (likely) has pstore data" ]
hasPstore() {
- if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+ if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then
false
fi
}
@@ -55,7 +86,7 @@
Returns the property value" ]
get_property() {
- adb shell getprop ${1} 2>&1 </dev/null
+ adb_sh getprop ${1} 2>&1 </dev/null
}
[ "USAGE: isDebuggable
@@ -89,18 +120,18 @@
Returns: true if device supports and set boot reason injection" ]
setBootloaderBootReason() {
inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
- if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+ if [ -z "`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`" ]; then
echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
return 1
fi
checkDebugBuild || return 1
- if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+ if adb_su "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" </dev/null |
grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
echo " does not set androidboot.bootreason kernel parameter." >&2
return 1
fi
- adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+ adb_su setprop persist.test.boot.reason "'${1}'" 2>/dev/null </dev/null
test_reason="`get_property persist.test.boot.reason`"
if [ X"${test_reason}" != X"${1}" ]; then
echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
@@ -299,7 +330,14 @@
return ${save_ret}
}
-[ "USAGE: report_bootstat_logs <expected> ...
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+ adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...
if not prefixed with a minus (-), <expected> will become a series of expected
matches:
@@ -314,8 +352,11 @@
report_bootstat_logs() {
save_ret=${?}
match=
+ timestamp=-d
for i in "${@}"; do
- if [ X"${i}" != X"${i#-}" ] ; then
+ if [ X"${i}" != X"${i#-t}" ]; then
+ timestamp="${i}"
+ elif [ X"${i}" != X"${i#-}" ]; then
match="${match}
${i#-}"
else
@@ -323,12 +364,13 @@
bootstat: Canonical boot reason: ${i}"
fi
done
- adb logcat -b all -d |
+ adb logcat -b all ${timestamp} |
grep bootstat[^e] |
grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
bootstat: Service started: /system/bin/bootstat -l
bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
@@ -341,6 +383,8 @@
init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
init : processing action (boot) from (/system/etc/init/bootstat.rc
init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc
+init : processing action (zygote-start) from (/system/etc/init/bootstat.rc
init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
(/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
(/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
@@ -355,6 +399,8 @@
(/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
(/system/bin/bootstat --record_time_since_factory_reset)'...
(/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat --set_system_boot_reason)'...
+ (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}
(/system/bin/bootstat -l)'...
(/system/bin/bootstat -l)' (pid " |
grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
@@ -613,7 +659,7 @@
test_optional_ota() {
checkDebugBuild || return
duration_test
- adb shell su root touch /data/misc/bootstat/build_date >&2
+ adb_su touch /data/misc/bootstat/build_date >&2 </dev/null
adb reboot ota
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,ota
@@ -679,7 +725,7 @@
test_factory_reset() {
checkDebugBuild || return
duration_test
- adb shell su root rm /data/misc/bootstat/build_date >&2
+ adb_su rm /data/misc/bootstat/build_date >&2 </dev/null
adb reboot >&2
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
@@ -715,7 +761,7 @@
wait_for_screen
( exit ${save_ret} ) # because one can not just do ?=${save_ret}
EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
- EXPECT_PROPERTY sys.boot.reason.last ""
+ EXPECT_PROPERTY sys.boot.reason.last "\(\|bootloader\)"
check_boilerplate_properties
report_bootstat_logs reboot,factory_reset bootloader \
"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
@@ -766,12 +812,12 @@
enterPstore
# Send it _many_ times to combat devices with flakey pstore
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
- echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
wait_for_screen
- adb shell su root \
+ adb_su </dev/null \
cat /proc/fs/pstore/console-ramoops \
/proc/fs/pstore/console-ramoops-0 2>/dev/null |
grep 'healthd: battery l=' |
@@ -780,7 +826,7 @@
if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
# retry
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
- echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null
done
adb reboot cold >&2
adb wait-for-device
@@ -806,7 +852,7 @@
test_optional_battery() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,battery
+ adb_sh setprop sys.powerctl shutdown,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -827,7 +873,7 @@
test_optional_battery_thermal() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,thermal,battery
+ adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -866,7 +912,7 @@
panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
pstore_ok=true
fi
- echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -893,8 +939,8 @@
panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
pstore_ok=true
fi
- echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
- echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ echo "SysRq : Trigger a crash : 'test'" | adb_su tee /dev/kmsg
+ echo c | adb_su tee /proc/sysrq-trigger >/dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}
@@ -924,7 +970,7 @@
pstore_ok=true
fi
echo "Kernel panic - not syncing: hung_task: blocked tasks" |
- adb shell su root tee /dev/kmsg
+ adb_su tee /dev/kmsg
adb reboot warm
wait_for_screen
EXPECT_PROPERTY sys.boot.reason ${panic_msg}
@@ -956,7 +1002,7 @@
test_thermal_shutdown() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,thermal
+ adb_sh setprop sys.powerctl shutdown,thermal </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -977,7 +1023,7 @@
test_userrequested_shutdown() {
duration_test ">60"
echo " power on request" >&2
- adb shell setprop sys.powerctl shutdown,userrequested
+ adb_sh setprop sys.powerctl shutdown,userrequested </dev/null
sleep 5
echo -n "WARNING: Please power device back up, waiting ... " >&2
wait_for_screen -n >&2
@@ -996,7 +1042,7 @@
- NB: should report reboot,shell" ]
test_shell_reboot() {
duration_test
- adb shell reboot
+ adb_sh reboot </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,shell
EXPECT_PROPERTY sys.boot.reason.last reboot,shell
@@ -1032,7 +1078,7 @@
test_optional_rescueparty() {
blind_reboot_test
echo "WARNING: legacy devices are allowed to fail following ro.boot.bootreason result" >&2
- EXPECT_PROPERTY ro.boot.bootreason reboot,rescueparty
+ EXPECT_PROPERTY ro.boot.bootreason '\(reboot\|reboot,rescueparty\)'
}
[ "USAGE: test_Its_Just_So_Hard_reboot
@@ -1049,7 +1095,7 @@
else
duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`
fi
- adb shell 'reboot "Its Just So Hard"'
+ adb_sh 'reboot "Its Just So Hard"' </dev/null
wait_for_screen
EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard
@@ -1146,7 +1192,121 @@
run_bootloader
}
-[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+[ "USAGE: run_kBootReasonMap [--boot_reason_enum] value expected
+
+bootloader boot reason injection tests:
+- if --boot_reason_enum run bootstat executable for result instead.
+- inject boot reason into sys.boot.reason
+- run bootstat --set_system_boot_reason
+- check for expected enum
+- " ]
+run_kBootReasonMap() {
+ if [ X"--boot_reason_enum" = X"${1}" ]; then
+ shift
+ local sys_expected="${1}"
+ shift
+ local enum_expected="${1}"
+ adb_su bootstat --boot_reason_enum="${sys_expected}" |
+ (
+ local retval=-1
+ while read -r id match; do
+ if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then
+ retval=0
+ fi
+ if [ ${enum_expected} != ${id} ]; then
+ echo "ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}" >&2
+ retval=1
+ fi
+ done
+ exit ${retval}
+ )
+ return
+ fi
+ local sys_expected="${1}"
+ shift
+ local enum_expected="${1}"
+ adb_su setprop sys.boot.reason "${sys_expected}" </dev/null
+ adb_su bootstat --record_boot_reason </dev/null
+ # Check values
+ EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+ local retval=${?}
+ local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`
+ [ "${enum_expected}" = "${result}" ] ||
+ (
+ [ -n "${result}" ] || result="<nothing>"
+ echo "ERROR: ${enum_expected} ${sys_expected} got ${result}" >&2
+ false
+ ) ||
+ retval=${?}
+ return ${retval}
+}
+
+[ "USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout
+
+convert any regex expressions into a series of non-regex test strings" ]
+filter_kBootReasonMap() {
+ while read -r id match; do
+ case ${match} in
+ 'reboot,[empty]')
+ echo ${id} # matches b/c of special case
+ echo ${id} reboot,y # matches b/c of regex
+ echo 1 reboot,empty # negative test (ID for unknown is 1)
+ ;;
+ reboot)
+ echo 1 reboog # negative test (ID for unknown is 1)
+ ;;
+ 'reboot,pmic_off_fault,.*')
+ echo ${id} reboot,pmic_off_fault,hello,world
+ echo ${id} reboot,pmic_off_fault,
+ echo 1 reboot,pmic_off_fault
+ ;;
+ esac
+ echo ${id} "${match}" # matches b/c of exact
+ done
+}
+
+[ "USAGE: test_kBootReasonMap
+
+kBootReasonMap test
+- (wait until screen is up, boot has completed)
+- read bootstat for kBootReasonMap entries and test them all" ]
+test_kBootReasonMap() {
+ checkDebugBuild || return
+ duration_test 15
+ local tempfile="`mktemp`"
+ local arg=--boot_reason_enum
+ adb_su bootstat ${arg} </dev/null 2>/dev/null |
+ filter_kBootReasonMap >${tempfile}
+ if [ ! -s "${tempfile}" ]; then
+ wait_for_screen
+ arg=
+ sed -n <${progpath}bootstat.cpp \
+ '/kBootReasonMap = {/,/^};/s/.*{"\([^"]*\)", *\([0-9][0-9]*\)},.*/\2 \1/p' |
+ sed 's/\\\\/\\/g' |
+ filter_kBootReasonMap >${tempfile}
+ fi
+ T=`adb_date`
+ retval=0
+ while read -r enum string; do
+ if [ X"${string}" != X"${string#*[[].[]]}" -o X"${string}" != X"${string#*\\.}" ]; then
+ if [ 'reboot\.empty' != "${string}" ]; then
+ echo "WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}" >&2
+ enum=1
+ fi
+ fi
+ run_kBootReasonMap ${arg} "${string}" "${enum}" </dev/null || retval=${?}
+ done <${tempfile}
+ rm ${tempfile}
+ ( exit ${retval} )
+ # See filter_kBootReasonMap() for negative tests and add them here too
+ report_bootstat_logs -t${T} \
+ '-bootstat: Service started: bootstat --boot_reason_enum=' \
+ '-bootstat: Unknown boot reason: reboot,empty' \
+ '-bootstat: Unknown boot reason: reboog' \
+ '-bootstat: Unknown boot reason: reboot,pmic_off_fault'
+}
+
+[ "USAGE: ${progname} [-s SERIAL] [tests]...
Mainline executive to run the above tests" ]
@@ -1161,7 +1321,7 @@
if [ X"--macros" != X"${1}" ]; then
if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
- echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+ echo "USAGE: ${progname} [-s SERIAL] [tests]..."
echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
exit 0
fi
@@ -1210,7 +1370,7 @@
Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \
bootloader_kernel_panic bootloader_oem_powerkey \
bootloader_wdog_reset bootloader_cold bootloader_warm \
- bootloader_hard bootloader_recovery
+ bootloader_hard bootloader_recovery kBootReasonMap
fi
if [ X"nothing" = X"${1}" ]; then
shift 1
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6700b6c..cd9fda3 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -89,7 +89,7 @@
}
void ShowHelp(const char* cmd) {
- fprintf(stderr, "Usage: %s [options]\n", cmd);
+ fprintf(stderr, "Usage: %s [options]...\n", cmd);
fprintf(stderr,
"options include:\n"
" -h, --help Show this help\n"
@@ -99,7 +99,8 @@
" --value Optional value to associate with the boot event\n"
" --record_boot_complete Record metrics related to the time for the device boot\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");
+ " --record_time_since_factory_reset Record the time since the device was reset\n"
+ " --boot_reason_enum=<reason> Report the match to the kBootReasonMap table\n");
}
// Constructs a readable, printable string from the givencommand line
@@ -114,34 +115,16 @@
return cmd;
}
-// Convenience wrapper over the property API that returns an
-// std::string.
-std::string GetProperty(const char* key) {
- std::vector<char> temp(PROPERTY_VALUE_MAX);
- const int len = property_get(key, &temp[0], nullptr);
- if (len < 0) {
- return "";
- }
- return std::string(&temp[0], len);
-}
-
-bool SetProperty(const char* key, const std::string& val) {
- return property_set(key, val.c_str()) == 0;
-}
-
-bool SetProperty(const char* key, const char* val) {
- return property_set(key, val) == 0;
-}
-
constexpr int32_t kEmptyBootReason = 0;
constexpr int32_t kUnknownBootReason = 1;
// A mapping from boot reason string, as read from the ro.boot.bootreason
// system property, to a unique integer ID. Viewers of log data dashboards for
// the boot_reason metric may refer to this mapping to discern the histogram
-// values.
+// values. Regex matching, to manage the scale, as a minimum require either
+// [, \ or * to be present in the string to switch to checking.
const std::map<std::string, int32_t> kBootReasonMap = {
- {"empty", kEmptyBootReason},
+ {"reboot,[empty]", kEmptyBootReason},
{"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
{"normal", 2},
{"recovery", 3},
@@ -157,13 +140,13 @@
{"mba_err", 13},
{"Watchdog", 14},
{"Panic", 15},
- {"power_key", 16},
- {"power_on", 17},
+ {"power_key", 16}, // aliasReasons to cold,powerkey (Mediatek)
+ {"power_on", 17}, // aliasReasons to cold,powerkey
{"Reboot", 18},
{"rtc", 19},
{"edl", 20},
{"oem_pon1", 21},
- {"oem_powerkey", 22},
+ {"oem_powerkey", 22}, // aliasReasons to cold,powerkey
{"oem_unknown_reset", 23},
{"srto: HWWDT reset SC", 24},
{"srto: HWWDT reset platform", 25},
@@ -218,15 +201,15 @@
{"hard,hw_reset", 72},
{"shutdown,suspend", 73}, // Suspend to RAM
{"shutdown,hibernate", 74}, // Suspend to DISK
- {"power_on_key", 75},
- {"reboot_by_key", 76},
- {"wdt_by_pass_pwk", 77},
- {"reboot_longkey", 78},
- {"powerkey", 79},
- {"usb", 80},
- {"wdt", 81},
- {"tool_by_pass_pwk", 82},
- {"2sec_reboot", 83},
+ {"power_on_key", 75}, // aliasReasons to cold,powerkey
+ {"reboot_by_key", 76}, // translated to reboot,by_key
+ {"wdt_by_pass_pwk", 77}, // Mediatek
+ {"reboot_longkey", 78}, // translated to reboot,longkey
+ {"powerkey", 79}, // aliasReasons to cold,powerkey
+ {"usb", 80}, // aliasReasons to cold,charger (Mediatek)
+ {"wdt", 81}, // Mediatek
+ {"tool_by_pass_pwk", 82}, // aliasReasons to reboot,tool (Mediatek)
+ {"2sec_reboot", 83}, // aliasReasons to cold,rtc,2sec (Mediatek)
{"reboot,by_key", 84},
{"reboot,longkey", 85},
{"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
@@ -236,28 +219,28 @@
{"reboot,rescueparty", 90},
{"charge", 91},
{"oem_tz_crash", 92},
- {"uvlo", 93}, // aliasReasons converts to reboot,undervoltage
+ {"uvlo", 93}, // aliasReasons to reboot,undervoltage
{"oem_ps_hold", 94},
{"abnormal_reset", 95},
{"oemerr_unknown", 96},
{"reboot_fastboot_mode", 97},
{"watchdog_apps_bite", 98},
{"xpu_err", 99},
- {"power_on_usb", 100},
+ {"power_on_usb", 100}, // aliasReasons to cold,charger
{"watchdog_rpm", 101},
{"watchdog_nonsec", 102},
{"watchdog_apps_bark", 103},
{"reboot_dmverity_corrupted", 104},
- {"reboot_smpl", 105}, // aliasReasons converts to reboot,powerloss
+ {"reboot_smpl", 105}, // aliasReasons to reboot,powerloss
{"watchdog_sdi_apps_reset", 106},
- {"smpl", 107}, // aliasReasons converts to reboot,powerloss
+ {"smpl", 107}, // aliasReasons to reboot,powerloss
{"oem_modem_failed_to_powerup", 108},
{"reboot_normal", 109},
{"oem_lpass_cfg", 110},
{"oem_xpu_ns_error", 111},
- {"power_key_press", 112},
+ {"power_key_press", 112}, // aliasReasons to cold,powerkey
{"hardware_reset", 113},
- {"reboot_by_powerkey", 114},
+ {"reboot_by_powerkey", 114}, // aliasReasons to cold,powerkey (is this correct?)
{"reboot_verity", 115},
{"oem_rpm_undef_error", 116},
{"oem_crash_on_the_lk", 117},
@@ -267,7 +250,7 @@
{"factory_cable", 121},
{"oem_ar6320_failed_to_powerup", 122},
{"watchdog_rpm_bite", 123},
- {"power_on_cable", 124},
+ {"power_on_cable", 124}, // aliasReasons to cold,charger
{"reboot_unknown", 125},
{"wireless_charger", 126},
{"0x776655ff", 127},
@@ -293,10 +276,10 @@
{"software_master", 147},
{"cold,charger", 148},
{"cold,rtc", 149},
- {"cold,rtc,2sec", 150},
- {"reboot,tool", 151},
- {"reboot,wdt", 152},
- {"reboot,unknown", 153},
+ {"cold,rtc,2sec", 150}, // Mediatek
+ {"reboot,tool", 151}, // Mediatek
+ {"reboot,wdt", 152}, // Mediatek
+ {"reboot,unknown", 153}, // Mediatek
{"kernel_panic,audit", 154},
{"kernel_panic,atomic", 155},
{"kernel_panic,hung", 156},
@@ -312,6 +295,22 @@
{"kernel_panic,dsps", 166},
{"kernel_panic,wcnss", 167},
{"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168},
+ {"recovery,quiescent", 169},
+ {"reboot,quiescent", 170},
+ {"reboot,rtc", 171},
+ {"reboot,dm-verity_device_corrupted", 172},
+ {"reboot,dm-verity_enforcing", 173},
+ {"reboot,keys_clear", 174},
+ {"reboot,pmic_off_fault,.*", 175},
+ {"reboot,pmic_off_s3rst,.*", 176},
+ {"reboot,pmic_off_other,.*", 177},
+ {"reboot,userrequested,fastboot", 178},
+ {"reboot,userrequested,recovery", 179},
+ {"reboot,userrequested,recovery,ui", 180},
+ {"shutdown,userrequested,fastboot", 181},
+ {"shutdown,userrequested,recovery", 182},
+ {"reboot,unknown[0-9]*", 183},
+ {"reboot,longkey,.*", 184},
};
// Converts a string value representing the reason the system booted to an
@@ -327,6 +326,16 @@
return kEmptyBootReason;
}
+ for (const auto& [match, id] : kBootReasonMap) {
+ // Regex matches as a minimum require either [, \ or * to be present.
+ if (match.find_first_of("[\\*") == match.npos) continue;
+ // enforce match from beginning to end
+ auto exact = match;
+ if (exact[0] != '^') exact = "^" + exact;
+ if (exact[exact.size() - 1] != '$') exact = exact + "$";
+ if (std::regex_search(boot_reason, std::regex(exact))) return id;
+ }
+
LOG(INFO) << "Unknown boot reason: " << boot_reason;
return kUnknownBootReason;
}
@@ -746,11 +755,13 @@
void BootReasonAddToHistory(const std::string& system_boot_reason) {
if (system_boot_reason.empty()) return;
LOG(INFO) << "Canonical boot reason: " << system_boot_reason;
- auto old_system_boot_reason = GetProperty(system_reboot_reason_property);
- if (!SetProperty(system_reboot_reason_property, system_boot_reason)) {
- SetProperty(system_reboot_reason_property, system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
+ auto old_system_boot_reason = android::base::GetProperty(system_reboot_reason_property, "");
+ if (!android::base::SetProperty(system_reboot_reason_property, system_boot_reason)) {
+ android::base::SetProperty(system_reboot_reason_property,
+ system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));
}
- auto reason_history = android::base::Split(GetProperty(history_reboot_reason_property), "\n");
+ auto reason_history =
+ android::base::Split(android::base::GetProperty(history_reboot_reason_property, ""), "\n");
static auto mark = time(nullptr);
auto mark_str = std::string(",") + std::to_string(mark);
auto marked_system_boot_reason = system_boot_reason + mark_str;
@@ -773,7 +784,8 @@
reason_history.insert(reason_history.begin(), marked_system_boot_reason);
// If the property string is too long ( > PROPERTY_VALUE_MAX)
// we get an error, so trim out last entry and try again.
- while (!(SetProperty(history_reboot_reason_property, android::base::Join(reason_history, '\n')))) {
+ while (!android::base::SetProperty(history_reboot_reason_property,
+ android::base::Join(reason_history, '\n'))) {
auto it = std::prev(reason_history.end());
if (it == reason_history.end()) break;
reason_history.erase(it);
@@ -782,7 +794,7 @@
// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
std::string BootReasonStrToReason(const std::string& boot_reason) {
- std::string ret(GetProperty(system_reboot_reason_property));
+ auto ret = android::base::GetProperty(system_reboot_reason_property, "");
std::string reason(boot_reason);
// If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
if (reason == ret) ret = "";
@@ -827,12 +839,12 @@
// following table smaller.
static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
{"watchdog", "wdog"},
- {"cold,powerkey", "powerkey|power_key|PowerKey"},
{"kernel_panic", "panic"},
{"shutdown,thermal", "thermal"},
{"warm,s3_wakeup", "s3_wakeup"},
{"hard,hw_reset", "hw_reset"},
- {"cold,charger", "usb"},
+ {"cold,charger", "usb|power_on_cable"},
+ {"cold,powerkey", "powerkey|power_key|PowerKey|power_on"},
{"cold,rtc", "rtc"},
{"cold,rtc,2sec", "2sec_reboot"},
{"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
@@ -922,7 +934,7 @@
if (isBluntRebootReason(ret)) {
// Content buffer no longer will have console data. Beware if more
// checks added below, that depend on parsing console content.
- content = GetProperty(last_reboot_reason_property);
+ content = android::base::GetProperty(last_reboot_reason_property, "");
transformReason(content);
// Anything in last is better than 'super-blunt' reboot or shutdown.
@@ -966,7 +978,7 @@
static const std::string kBuildDateKey = "build_date";
std::string boot_complete_prefix = "boot_complete";
- std::string build_date_str = GetProperty("ro.build.date.utc");
+ auto build_date_str = android::base::GetProperty("ro.build.date.utc", "");
int32_t build_date;
if (!android::base::ParseInt(build_date_str, &build_date)) {
return std::string();
@@ -989,7 +1001,7 @@
// Records the value of a given ro.boottime.init property in milliseconds.
void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {
- std::string value = GetProperty(property);
+ auto value = android::base::GetProperty(property, "");
int32_t time_in_ms;
if (android::base::ParseInt(value, &time_in_ms)) {
@@ -1007,7 +1019,7 @@
// |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',
// where timeN is in milliseconds.
- std::string value = GetProperty("ro.boot.boottime");
+ auto value = android::base::GetProperty("ro.boot.boottime", "");
if (value.empty()) {
// ro.boot.boottime is not reported on all devices.
return BootloaderTimingMap();
@@ -1019,6 +1031,7 @@
auto stageTimingValues = android::base::Split(stageTiming, ":");
DCHECK_EQ(2U, stageTimingValues.size());
+ if (stageTimingValues.size() < 2) continue;
std::string stageName = stageTimingValues[0];
int32_t time_ms;
if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
@@ -1080,17 +1093,8 @@
void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
double time_since_last_boot_sec) {
- const std::string reason(GetProperty(bootloader_reboot_reason_property));
-
- if (reason.empty()) {
- android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, "<EMPTY>", "<EMPTY>",
- end_time.count(), total_duration.count(),
- (int64_t)bootloader_duration_ms,
- (int64_t)time_since_last_boot_sec * 1000);
- return;
- }
-
- const std::string system_reason(GetProperty(system_reboot_reason_property));
+ const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "<EMPTY>");
+ const auto system_reason = android::base::GetProperty(system_reboot_reason_property, "<EMPTY>");
android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
system_reason.c_str(), end_time.count(), total_duration.count(),
(int64_t)bootloader_duration_ms,
@@ -1098,19 +1102,20 @@
}
void SetSystemBootReason() {
- const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+ const auto bootloader_boot_reason =
+ android::base::GetProperty(bootloader_reboot_reason_property, "");
const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
// Record the scrubbed system_boot_reason to the property
BootReasonAddToHistory(system_boot_reason);
// Shift last_reboot_reason_property to last_last_reboot_reason_property
- std::string last_boot_reason(GetProperty(last_reboot_reason_property));
+ auto last_boot_reason = android::base::GetProperty(last_reboot_reason_property, "");
if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {
last_boot_reason = system_boot_reason;
} else {
transformReason(last_boot_reason);
}
- SetProperty(last_last_reboot_reason_property, last_boot_reason);
- SetProperty(last_reboot_reason_property, "");
+ android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);
+ android::base::SetProperty(last_reboot_reason_property, "");
}
// Gets the boot time offset. This is useful when Android is running in a
@@ -1176,6 +1181,7 @@
boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
+ RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.first_stage");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
@@ -1197,7 +1203,7 @@
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
- const std::string reason(GetProperty(bootloader_reboot_reason_property));
+ const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, "");
if (reason.empty()) {
// Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
@@ -1215,12 +1221,12 @@
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
// Log the scrubbed system_boot_reason.
- const std::string system_reason(GetProperty(system_reboot_reason_property));
+ const auto system_reason = android::base::GetProperty(system_reboot_reason_property, "");
int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
if (reason == "") {
- SetProperty(bootloader_reboot_reason_property, system_reason);
+ android::base::SetProperty(bootloader_reboot_reason_property, system_reason);
}
}
@@ -1274,6 +1280,19 @@
boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
}
+// List the associated boot reason(s), if arg is nullptr then all.
+void PrintBootReasonEnum(const char* arg) {
+ int value = -1;
+ if (arg != nullptr) {
+ value = BootReasonStrToEnum(arg);
+ }
+ for (const auto& [match, id] : kBootReasonMap) {
+ if ((value < 0) || (value == id)) {
+ printf("%u\t%s\n", id, match.c_str());
+ }
+ }
+}
+
} // namespace
int main(int argc, char** argv) {
@@ -1288,6 +1307,7 @@
static const char boot_complete_str[] = "record_boot_complete";
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
+ static const char boot_reason_enum_str[] = "boot_reason_enum";
static const struct option long_options[] = {
// clang-format off
{ "help", no_argument, NULL, 'h' },
@@ -1299,6 +1319,7 @@
{ boot_complete_str, no_argument, NULL, 0 },
{ boot_reason_str, no_argument, NULL, 0 },
{ factory_reset_str, no_argument, NULL, 0 },
+ { boot_reason_enum_str, optional_argument, NULL, 0 },
{ NULL, 0, NULL, 0 }
// clang-format on
};
@@ -1323,6 +1344,8 @@
RecordBootReason();
} else if (option_name == factory_reset_str) {
RecordFactoryReset();
+ } else if (option_name == boot_reason_enum_str) {
+ PrintBootReasonEnum(optarg);
} else {
LOG(ERROR) << "Invalid option: " << option_name;
}
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 1300a27..85caf25 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,7 +1,9 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
-# mirror bootloader boot reason to system boot reason
-on property:ro.boot.bootreason=*
+# Mirror bootloader boot reason to system boot reason
+# ro.boot.bootreason should be set by init already
+# before post-fs trigger
+on post-fs && property:ro.boot.bootreason=*
setprop sys.boot.reason ${ro.boot.bootreason}
on post-fs-data
@@ -66,11 +68,16 @@
on property:init.svc.zygote=stopping
setprop sys.logbootcomplete 0
+# Set boot reason
+on zygote-start
+ # Converts bootloader boot reason and persist.sys.boot.reason to system boot reason
+ # Need go after persist peroperties are loaded which is right before zygote-start trigger
+ exec_background - system log -- /system/bin/bootstat --set_system_boot_reason
+
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
- # Converts bootloader boot reason to system boot reason
# Record boot_complete and related stats (decryption, etc).
# Record the boot reason.
# Record time since factory reset.
# Log all boot events.
- exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+ exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/cpio/Android.bp b/cpio/Android.bp
new file mode 100644
index 0000000..baa0319
--- /dev/null
+++ b/cpio/Android.bp
@@ -0,0 +1,11 @@
+// Copyright 2005 The Android Open Source Project
+
+cc_binary_host {
+ name: "mkbootfs",
+ srcs: ["mkbootfs.c"],
+ cflags: ["-Werror"],
+ shared_libs: ["libcutils"],
+ dist: {
+ targets: ["dist_files"],
+ },
+}
diff --git a/cpio/Android.mk b/cpio/Android.mk
deleted file mode 100644
index 2aa7297..0000000
--- a/cpio/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- mkbootfs.c
-
-LOCAL_MODULE := mkbootfs
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 03b3287..2e226da 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -9,7 +9,6 @@
"-Wno-nullability-completeness",
"-Os",
],
- cpp_std: "gnu++17",
local_include_dirs: ["include"],
}
@@ -115,16 +114,16 @@
"libasync_safe",
"libbase",
"libdebuggerd",
- "libbacktrace",
"libunwindstack",
- "libdexfile",
+ "libdexfile_support_static", // libunwindstack dependency
"liblzma",
"libcutils",
],
target: {
recovery: {
- cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
- exclude_static_libs: ["libdexfile"],
+ exclude_static_libs: [
+ "libdexfile_support_static",
+ ],
},
},
@@ -144,6 +143,7 @@
shared_libs: [
"libbase",
"libcutils",
+ "libprocinfo",
],
export_header_lib_headers: ["libdebuggerd_common_headers"],
@@ -157,7 +157,6 @@
srcs: [
"libdebuggerd/backtrace.cpp",
- "libdebuggerd/elf_utils.cpp",
"libdebuggerd/open_files_list.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
@@ -170,13 +169,26 @@
include_dirs: ["bionic/libc"],
static_libs: [
- "libbacktrace",
+ "libdexfile_support_static", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
"libcutils",
"liblog",
],
+ target: {
+ recovery: {
+ exclude_static_libs: [
+ "libdexfile_support_static",
+ ],
+ },
+ },
+
+ product_variables: {
+ debuggable: {
+ cflags: ["-DROOT_POSSIBLE"],
+ },
+ },
}
cc_test {
@@ -206,18 +218,17 @@
},
shared_libs: [
- "libbacktrace",
"libbase",
"libcutils",
"libdebuggerd_client",
"liblog",
"libminijail",
"libnativehelper",
+ "libunwindstack",
],
static_libs: [
"libdebuggerd",
- "libunwindstack",
],
local_include_dirs: [
@@ -272,7 +283,6 @@
],
shared_libs: [
- "libbacktrace",
"libbase",
"liblog",
"libprocinfo",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 77f3515..1a5b435 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -22,10 +22,13 @@
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#include <chrono>
+#include <iomanip>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -33,6 +36,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
+#include <procinfo/process.h>
#include "debuggerd/handler.h"
#include "protocol.h"
@@ -40,7 +44,10 @@
using namespace std::chrono_literals;
+using android::base::ReadFileToString;
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
+using android::base::WriteStringToFd;
static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
@@ -62,8 +69,90 @@
tv->tv_usec = static_cast<long>(microseconds.count());
}
-bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
+static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
+ struct tm now;
+ time_t t = time(nullptr);
+ localtime_r(&t, &now);
+ char timestamp[32];
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
+ std::string time_now(timestamp);
+
+ std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
+
+ char proc_name_buf[1024];
+ const char* proc_name = nullptr;
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
+
+ if (fp) {
+ proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
+ }
+
+ if (!proc_name) {
+ proc_name = "<unknown>";
+ }
+
+ buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
+ << "Cmd line: " << proc_name << "\n";
+}
+
+static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
+ buffer << "----- end " << std::to_string(pid) << " -----\n";
+}
+
+/**
+ * Returns the wchan data for each thread in the process,
+ * or empty string if unable to obtain any data.
+ */
+static std::string get_wchan_data(pid_t pid) {
+ std::stringstream buffer;
+ std::vector<pid_t> tids;
+
+ if (!android::procinfo::GetProcessTids(pid, &tids)) {
+ LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
+ return buffer.str();
+ }
+
+ std::stringstream data;
+ for (int tid : tids) {
+ std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
+ std::string wchan_str;
+ if (!ReadFileToString(path, &wchan_str, true)) {
+ PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
+ continue;
+ }
+ data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
+ }
+
+ if (std::string str = data.str(); !str.empty()) {
+ get_wchan_header(pid, buffer);
+ buffer << "\n" << str << "\n";
+ get_wchan_footer(pid, buffer);
+ buffer << "\n";
+ }
+
+ return buffer.str();
+}
+
+static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
+ if (!WriteStringToFd(data, fd)) {
+ LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
+ }
+}
+
+bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
unique_fd output_fd) {
+ pid_t pid = tid;
+ if (dump_type == kDebuggerdJavaBacktrace) {
+ // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid.
+ android::procinfo::ProcessInfo procinfo;
+ std::string error;
+ if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
+ LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
+ return false;
+ }
+ pid = procinfo.pid;
+ }
+
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
@@ -133,15 +222,16 @@
PLOG(ERROR) << "failed to set pipe buffer size";
}
- if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+ pipe_write.reset();
+ if (rc != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
// Check to make sure we've successfully registered.
InterceptResponse response;
- ssize_t rc =
- TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
<< "timeout reached?";
@@ -162,7 +252,7 @@
return false;
}
- if (!send_signal(pid, dump_type)) {
+ if (!send_signal(tid, dump_type)) {
return false;
}
@@ -245,6 +335,17 @@
if (copy == -1) {
return -1;
}
+
+ // debuggerd_trigger_dump results in every thread in the process being interrupted
+ // by a signal, so we need to fetch the wchan data before calling that.
+ std::string wchan_data = get_wchan_data(tid);
+
int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
- return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
+ int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
+
+ // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
+ // kernel stack traces (/proc/*/stack).
+ dump_wchan_data(wchan_data, fd, tid);
+
+ return ret;
}
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 577e336..cb55745 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -48,7 +48,12 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
@@ -63,8 +68,6 @@
using android::base::unique_fd;
using android::base::StringPrintf;
-using unwindstack::Regs;
-
static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
struct stat st;
std::string task_path = StringPrintf("task/%d", tid);
@@ -287,7 +290,8 @@
case 1:
*abort_msg_address = crash_info->data.v1.abort_msg_address;
*siginfo = crash_info->data.v1.siginfo;
- regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->data.v1.ucontext));
+ regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
+ &crash_info->data.v1.ucontext));
break;
default:
@@ -348,8 +352,22 @@
return vm_pid;
}
+static void InstallSigPipeHandler() {
+ struct sigaction action = {};
+ action.sa_handler = SIG_IGN;
+ action.sa_flags = SA_RESTART;
+ sigaction(SIGPIPE, &action, nullptr);
+}
+
int main(int argc, char** argv) {
DefuseSignalHandlers();
+ InstallSigPipeHandler();
+
+ // There appears to be a bug in the kernel where our death causes SIGHUP to
+ // be sent to our process group if we exit while it has stopped jobs (e.g.
+ // because of wait_for_gdb). Use setsid to create a new process group to
+ // avoid hitting this.
+ setsid();
atrace_begin(ATRACE_TAG, "before reparent");
pid_t target_process = getppid();
@@ -445,6 +463,7 @@
ThreadInfo info;
info.pid = target_process;
info.tid = thread;
+ info.uid = getuid();
info.process_name = process_name;
info.thread_name = get_thread_name(thread);
@@ -461,7 +480,7 @@
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
} else {
- info.registers.reset(Regs::RemoteGet(thread));
+ info.registers.reset(unwindstack::Regs::RemoteGet(thread));
if (!info.registers) {
PLOG(WARNING) << "failed to fetch registers for thread " << thread;
ptrace(PTRACE_DETACH, thread, 0, 0);
@@ -554,30 +573,25 @@
}
// TODO: Use seccomp to lock ourselves down.
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(vm_pid, false));
- if (!map) {
- LOG(FATAL) << "failed to create backtrace map";
- }
-
- std::shared_ptr<unwindstack::Memory> process_memory = map->GetProcessMemory();
- if (!process_memory) {
- LOG(FATAL) << "failed to get unwindstack::Memory handle";
+ unwindstack::UnwinderFromPid unwinder(256, vm_pid);
+ if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ LOG(FATAL) << "Failed to init unwinder object.";
}
std::string amfd_data;
if (backtrace) {
ATRACE_NAME("dump_backtrace");
- dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
+ dump_backtrace(std::move(g_output_fd), &unwinder, thread_info, g_target_thread);
} else {
{
ATRACE_NAME("fdsan table dump");
- populate_fdsan_table(&open_files, process_memory, fdsan_table_address);
+ populate_fdsan_table(&open_files, unwinder.GetProcessMemory(), fdsan_table_address);
}
{
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
- g_target_thread, abort_msg_address, &open_files, &amfd_data);
+ engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
+ abort_msg_address, &open_files, &amfd_data);
}
}
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0bdfbf..3041664 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -193,6 +193,7 @@
fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n");
fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n");
#endif
+ fprintf(stderr, " xom read execute-only memory\n");
fprintf(stderr, "\n");
fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n");
fprintf(stderr, " LOG_ALWAYS_FATAL_IF call liblog LOG_ALWAYS_FATAL_IF\n");
@@ -314,6 +315,11 @@
} else if (!strcasecmp(arg, "seccomp")) {
set_system_seccomp_filter();
syscall(99999);
+#if defined(__LP64__)
+ } else if (!strcasecmp(arg, "xom")) {
+ // Try to read part of our code, which will fail if XOM is active.
+ printf("*%lx = %lx\n", reinterpret_cast<long>(usage), *reinterpret_cast<long*>(usage));
+#endif
#if defined(__arm__)
} else if (!strcasecmp(arg, "kuser_helper_version")) {
return __kuser_helper_version;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index bea8b43..fbc8b97 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -32,6 +32,7 @@
#include <android/fdsan.h>
#include <android/set_abort_message.h>
+#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -53,6 +54,8 @@
#include "util.h"
using namespace std::chrono_literals;
+
+using android::base::SendFileDescriptors;
using android::base::unique_fd;
#if defined(__LP64__)
@@ -123,12 +126,14 @@
ASSERT_GE(pipe_buffer_size, 1024 * 1024);
- if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());
+ output_pipe_write.reset();
+ if (rc != sizeof(req)) {
FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
}
InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
if (rc == -1) {
FAIL() << "failed to read response from tombstoned: " << strerror(errno);
} else if (rc == 0) {
@@ -238,9 +243,10 @@
void CrasherTest::AssertDeath(int signo) {
int status;
- pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
+ pid_t pid = TIMEOUT(10, waitpid(crasher_pid, &status, 0));
if (pid != crasher_pid) {
- printf("failed to wait for crasher (pid %d)\n", crasher_pid);
+ printf("failed to wait for crasher (expected pid %d, return value %d): %s\n", crasher_pid, pid,
+ strerror(errno));
sleep(100);
FAIL() << "failed to wait for crasher: " << strerror(errno);
}
@@ -1093,3 +1099,30 @@
// This should be good enough, though...
ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
}
+
+static __attribute__((__noinline__)) void overflow_stack(void* p) {
+ void* buf[1];
+ buf[0] = p;
+ static volatile void* global = buf;
+ if (global) {
+ global = buf;
+ overflow_stack(&buf);
+ }
+}
+
+TEST_F(CrasherTest, stack_overflow) {
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([]() { overflow_stack(nullptr); });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGSEGV);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 15c0265..bbec612 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,9 +42,12 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
#include "debuggerd/handler.h"
#include "handler/fallback.h"
@@ -55,7 +58,6 @@
#include "libdebuggerd/tombstone.h"
using android::base::unique_fd;
-using unwindstack::Regs;
extern "C" bool __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator();
@@ -73,17 +75,22 @@
}
{
- std::unique_ptr<Regs> regs;
+ std::unique_ptr<unwindstack::Regs> regs;
ThreadInfo thread;
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
- thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+ unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
+ thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
// TODO: Create this once and store it in a global?
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
- dump_backtrace_thread(output_fd, map.get(), thread);
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
+ if (unwinder.Init(arch)) {
+ dump_backtrace_thread(output_fd, &unwinder, thread);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
+ }
}
__linker_disable_fallback_allocator();
}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index a064ca0..598ea85 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -268,8 +268,15 @@
_exit(errno);
}
- // Exit immediately on both sides of the fork.
- // crash_dump is ptracing us, so it'll get to do whatever it wants in between.
+ // crash_dump is ptracing both sides of the fork; it'll let the parent exit,
+ // but keep the orphan stopped to peek at its memory.
+
+ // There appears to be a bug in the kernel where our death causes SIGHUP to
+ // be sent to our process group if we exit while it has stopped jobs (e.g.
+ // because of wait_for_gdb). Use setsid to create a new process group to
+ // avoid hitting this.
+ setsid();
+
_exit(0);
}
@@ -383,7 +390,9 @@
execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
nullptr, nullptr);
- fatal_errno("exec failed");
+ async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s",
+ strerror(errno));
+ return 1;
}
input_write.reset();
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index f0a01f4..c606970 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -35,8 +35,8 @@
#include <string>
#include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
#include <log/log.h>
+#include <unwindstack/Unwinder.h>
#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
@@ -59,25 +59,25 @@
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
-void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
+void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+ const ThreadInfo& thread) {
log_t log;
log.tfd = output_fd;
log.amfd_data = nullptr;
_LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
- std::vector<backtrace_frame_data_t> frames;
- if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
+ unwinder->SetRegs(thread.registers.get());
+ unwinder->Unwind();
+ if (unwinder->NumFrames() == 0) {
_LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
return;
}
- for (auto& frame : frames) {
- _LOG(&log, logtype::BACKTRACE, " %s\n", Backtrace::FormatFrameData(&frame).c_str());
- }
+ log_backtrace(&log, unwinder, " ");
}
-void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
log_t log;
log.tfd = output_fd.get();
@@ -91,10 +91,10 @@
dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
- dump_backtrace_thread(output_fd.get(), map, target->second);
+ dump_backtrace_thread(output_fd.get(), unwinder, target->second);
for (const auto& [tid, info] : thread_info) {
if (tid != target_thread) {
- dump_backtrace_thread(output_fd.get(), map, info);
+ dump_backtrace_thread(output_fd.get(), unwinder, info);
}
}
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
deleted file mode 100644
index d7afc0b..0000000
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "DEBUG"
-
-#include "libdebuggerd/elf_utils.h"
-
-#include <elf.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <unwindstack/Memory.h>
-
-#define NOTE_ALIGN(size) (((size) + 3) & ~3)
-
-template <typename HdrType, typename PhdrType, typename NhdrType>
-static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident,
- std::string* build_id) {
- HdrType hdr;
-
- memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
-
- // First read the rest of the header.
- if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
- sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
- return false;
- }
-
- for (size_t i = 0; i < hdr.e_phnum; i++) {
- PhdrType phdr;
- if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
- reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
- return false;
- }
- // Looking for the .note.gnu.build-id note.
- if (phdr.p_type == PT_NOTE) {
- size_t hdr_size = phdr.p_filesz;
- uintptr_t addr = base_addr + phdr.p_offset;
- while (hdr_size >= sizeof(NhdrType)) {
- NhdrType nhdr;
- if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
- return false;
- }
- addr += sizeof(nhdr);
- if (nhdr.n_type == NT_GNU_BUILD_ID) {
- // Skip the name (which is the owner and should be "GNU").
- addr += NOTE_ALIGN(nhdr.n_namesz);
- uint8_t build_id_data[160];
- if (nhdr.n_descsz > sizeof(build_id_data)) {
- ALOGE("Possible corrupted note, desc size value is too large: %u",
- nhdr.n_descsz);
- return false;
- }
- if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
- return false;
- }
-
- build_id->clear();
- for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
- *build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
- }
-
- return true;
- } else {
- // Move past the extra note data.
- hdr_size -= sizeof(nhdr);
- size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
- addr += skip_bytes;
- if (hdr_size < skip_bytes) {
- break;
- }
- hdr_size -= skip_bytes;
- }
- }
- }
- }
- return false;
-}
-
-bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) {
- // Read and verify the elf magic number first.
- uint8_t e_ident[EI_NIDENT];
- if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
- return false;
- }
-
- if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
- return false;
- }
-
- // Read the rest of EI_NIDENT.
- if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
- return false;
- }
-
- if (e_ident[EI_CLASS] == ELFCLASS32) {
- return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(memory, addr, e_ident, build_id);
- } else if (e_ident[EI_CLASS] == ELFCLASS64) {
- return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
- }
-
- return false;
-}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index 119e59b..c20d090 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -28,15 +28,19 @@
#include "types.h"
#include "utility.h"
-class BacktraceMap;
+// Forward delcaration
+namespace unwindstack {
+class Unwinder;
+}
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
-void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
void dump_backtrace_header(int output_fd);
-void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread);
+void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+ const ThreadInfo& thread);
void dump_backtrace_footer(int output_fd);
#endif // _DEBUGGERD_BACKTRACE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index be90d0f..7133f77 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -29,7 +29,13 @@
#include "open_files_list.h"
#include "types.h"
-class BacktraceMap;
+// Forward declarations
+namespace unwindstack {
+class Unwinder;
+}
+
+// The maximum number of frames to save when unwinding.
+constexpr size_t kMaxFrames = 256;
/* Create and open a tombstone file for writing.
* Returns a writable file descriptor, or -1 with errno set appropriately.
@@ -38,16 +44,15 @@
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
-void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
- pid_t pid, pid_t tid, const std::string& process_name,
- const std::map<pid_t, std::string>& threads, uint64_t abort_msg_address,
- std::string* amfd_data);
+void engrave_tombstone(int tombstone_fd, unwindstack::Unwinder* unwinder,
+ const OpenFilesList* open_files, pid_t pid, pid_t tid,
+ const std::string& process_name, const std::map<pid_t, std::string>& threads,
+ uint64_t abort_msg_address, std::string* amfd_data);
void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
-void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map,
- unwindstack::Memory* process_memory,
+void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
std::string* amfd_data);
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 70583af..eb4b1b8 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -23,6 +23,9 @@
struct ThreadInfo {
std::unique_ptr<unwindstack::Regs> registers;
+
+ pid_t uid;
+
pid_t tid;
std::string thread_name;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 7c5304e..f189c45 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -18,6 +18,7 @@
#ifndef _DEBUGGERD_UTILITY_H
#define _DEBUGGERD_UTILITY_H
+#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -25,7 +26,6 @@
#include <string>
#include <android-base/macros.h>
-#include <backtrace/Backtrace.h>
struct log_t {
// Tombstone file descriptor.
@@ -61,13 +61,24 @@
OPEN_FILES
};
+#if defined(__LP64__)
+#define PRIPTR "016" PRIx64
+typedef uint64_t word_t;
+#else
+#define PRIPTR "08" PRIx64
+typedef uint32_t word_t;
+#endif
+
// Log information onto the tombstone.
void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
namespace unwindstack {
+class Unwinder;
class Memory;
}
+void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+
void dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);
void read_with_default(const char* path, char* buf, size_t len, const char* default_value);
diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h
deleted file mode 100644
index e7dbed7..0000000
--- a/debuggerd/libdebuggerd/test/BacktraceMock.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
-
-#include <backtrace/BacktraceMap.h>
-
-class BacktraceMapMock : public BacktraceMap {
- public:
- BacktraceMapMock() : BacktraceMap(0) {}
- virtual ~BacktraceMapMock() {}
-
- void AddMap(backtrace_map_t& map) {
- maps_.push_back(map);
- }
-};
-
-#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
new file mode 100644
index 0000000..023a578
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Unwinder.h>
+
+class UnwinderMock : public unwindstack::Unwinder {
+ public:
+ UnwinderMock() : Unwinder(128, new unwindstack::Maps, nullptr) {}
+ virtual ~UnwinderMock() { delete GetMaps(); }
+
+ void MockAddMap(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, std::string name,
+ uint64_t load_bias) {
+ GetMaps()->Add(start, end, offset, flags, name, load_bias);
+ }
+
+ void MockSetBuildID(uint64_t offset, const std::string& build_id) {
+ unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
+ if (map_info != nullptr) {
+ std::string* new_build_id = new std::string(build_id);
+ map_info->build_id = reinterpret_cast<uintptr_t>(new_build_id);
+ }
+ }
+};
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index d7036fd..3e920eb 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -20,10 +20,9 @@
#include <string>
+#include <android-base/file.h>
#include <gtest/gtest.h>
-#include "android-base/test_utils.h"
-
#include "libdebuggerd/open_files_list.h"
// Check that we can produce a list of open files for the current process, and
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 421ce43..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -15,6 +15,8 @@
*/
#include <stdlib.h>
+#include <sys/mman.h>
+#include <time.h>
#include <memory>
#include <string>
@@ -25,26 +27,16 @@
#include "libdebuggerd/utility.h"
-#include "BacktraceMock.h"
-#include "elf_fake.h"
+#include "UnwinderMock.h"
#include "host_signal_fixup.h"
#include "log_fake.h"
#include "tombstone.cpp"
-void dump_registers(log_t*, pid_t) {
-}
-
-void dump_memory_and_code(log_t*, Backtrace*) {
-}
-
-void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
-}
-
class TombstoneTest : public ::testing::Test {
protected:
virtual void SetUp() {
- map_mock_.reset(new BacktraceMapMock());
+ unwinder_mock_.reset(new UnwinderMock());
char tmp_file[256];
const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@@ -70,7 +62,6 @@
log_.should_retrieve_logcat = false;
resetLogs();
- elf_set_fake_build_id("");
}
virtual void TearDown() {
@@ -79,24 +70,20 @@
}
}
- std::unique_ptr<BacktraceMapMock> map_mock_;
+ std::unique_ptr<UnwinderMock> unwinder_mock_;
log_t log_;
std::string amfd_data_;
};
TEST_F(TombstoneTest, single_map) {
- backtrace_map_t map;
#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
+ unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0);
#else
- map.start = 0x1234000;
- map.end = 0x1235000;
+ unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0);
#endif
- map_mock_->AddMap(map);
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
+ dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -118,20 +105,25 @@
}
TEST_F(TombstoneTest, single_map_elf_build_id) {
- backtrace_map_t map;
+ uint64_t build_id_offset;
#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
+ build_id_offset = 0x123456789abcd000UL;
+ unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ,
+ "/system/lib/libfake.so", 0);
#else
- map.start = 0x1234000;
- map.end = 0x1235000;
+ build_id_offset = 0x1234000;
+ unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0);
#endif
- map.flags = PROT_READ;
- map.name = "/system/lib/libfake.so";
- map_mock_->AddMap(map);
- elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
+ unwinder_mock_->MockSetBuildID(
+ build_id_offset,
+ std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef),
+ static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56),
+ static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab),
+ static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12),
+ static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78),
+ static_cast<char>(0x90)});
+ dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -152,83 +144,15 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-// Even though build id is present, it should not be printed in either of
-// these cases.
-TEST_F(TombstoneTest, single_map_no_build_id) {
- backtrace_map_t map;
-#if defined(__LP64__)
- map.start = 0x123456789abcd000UL;
- map.end = 0x123456789abdf000UL;
-#else
- map.start = 0x1234000;
- map.end = 0x1235000;
-#endif
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.name = "/system/lib/libfake.so";
- map_mock_->AddMap(map);
-
- elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
-
- std::string tombstone_contents;
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- const char* expected_dump = \
-"\nmemory map (2 entries):\n"
-#if defined(__LP64__)
-" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
-" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
-#else
-" 01234000-01234fff -w- 0 1000\n"
-" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n";
-#endif
- ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
-
- ASSERT_STREQ("", amfd_data_.c_str());
-
- // Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
-}
-
TEST_F(TombstoneTest, multiple_maps) {
- backtrace_map_t map;
+ unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0);
+ unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0);
+ unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+ unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+ unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/system/lib/fake.so", 0);
- map.start = 0xa234000;
- map.end = 0xa235000;
- map_mock_->AddMap(map);
-
- map.start = 0xa334000;
- map.end = 0xa335000;
- map.offset = 0xf000;
- map.flags = PROT_READ;
- map_mock_->AddMap(map);
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_bias = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_bias = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
+ dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -258,31 +182,12 @@
}
TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
- backtrace_map_t map;
+ unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+ unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+ unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/system/lib/fake.so", 0);
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_bias = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_bias = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000);
+ dump_all_maps(&log_, unwinder_mock_.get(), 0x1000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -310,31 +215,12 @@
}
TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
- backtrace_map_t map;
+ unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+ unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+ unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/system/lib/fake.so", 0);
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_bias = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_bias = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000);
+ dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -362,31 +248,12 @@
}
TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
- backtrace_map_t map;
+ unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+ unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+ unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/system/lib/fake.so", 0);
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_bias = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_bias = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
-
- dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040);
+ dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -412,36 +279,17 @@
}
TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
- backtrace_map_t map;
-
- map.start = 0xa434000;
- map.end = 0xa435000;
- map.offset = 0x1000;
- map.load_bias = 0xd000;
- map.flags = PROT_WRITE;
- map_mock_->AddMap(map);
-
- map.start = 0xa534000;
- map.end = 0xa535000;
- map.offset = 0x3000;
- map.load_bias = 0x2000;
- map.flags = PROT_EXEC;
- map_mock_->AddMap(map);
-
- map.start = 0xa634000;
- map.end = 0xa635000;
- map.offset = 0;
- map.load_bias = 0;
- map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
- map.name = "/system/lib/fake.so";
- map_mock_->AddMap(map);
+ unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+ unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+ unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/system/lib/fake.so", 0);
#if defined(__LP64__)
uint64_t addr = 0x12345a534040UL;
#else
uint64_t addr = 0xf534040UL;
#endif
- dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
+ dump_all_maps(&log_, unwinder_mock_.get(), addr);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@@ -494,3 +342,484 @@
expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
}
+
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+ dump_thread_info(&log_, ThreadInfo{.uid = 1,
+ .pid = 2,
+ .tid = 3,
+ .thread_name = "some_thread",
+ .process_name = "some_process"});
+ std::string expected = "pid: 2, tid: 3, name: some_thread >>> some_process <<<\nuid: 1\n";
+ ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_timestamp) {
+ setenv("TZ", "UTC", 1);
+ tzset();
+ dump_timestamp(&log_, 0);
+ ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
+}
+
+class MemoryPattern : public unwindstack::Memory {
+ public:
+ MemoryPattern() = default;
+ virtual ~MemoryPattern() = default;
+
+ size_t Read(uint64_t, void* dst, size_t size) override {
+ uint8_t* data = reinterpret_cast<uint8_t*>(dst);
+ for (size_t i = 0; i < size; i++) {
+ data[i] = (i % 0xff);
+ }
+ return size;
+ }
+};
+
+TEST_F(TombstoneTest, dump_stack_single_frame) {
+ std::vector<unwindstack::FrameData> frames;
+ unwindstack::Maps maps;
+ MemoryPattern memory;
+
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+ dump_stack(&log_, frames, &maps, &memory);
+
+ std::string contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+ std::string expected =
+#if defined(__LP64__)
+ " 0000000000001f80 0706050403020100\n"
+ " 0000000000001f88 0f0e0d0c0b0a0908\n"
+ " 0000000000001f90 1716151413121110\n"
+ " 0000000000001f98 1f1e1d1c1b1a1918\n"
+ " 0000000000001fa0 2726252423222120\n"
+ " 0000000000001fa8 2f2e2d2c2b2a2928\n"
+ " 0000000000001fb0 3736353433323130\n"
+ " 0000000000001fb8 3f3e3d3c3b3a3938\n"
+ " 0000000000001fc0 4746454443424140\n"
+ " 0000000000001fc8 4f4e4d4c4b4a4948\n"
+ " 0000000000001fd0 5756555453525150\n"
+ " 0000000000001fd8 5f5e5d5c5b5a5958\n"
+ " 0000000000001fe0 6766656463626160\n"
+ " 0000000000001fe8 6f6e6d6c6b6a6968\n"
+ " 0000000000001ff0 7776757473727170\n"
+ " 0000000000001ff8 7f7e7d7c7b7a7978\n"
+ " #00 0000000000002000 0706050403020100\n"
+ " 0000000000002008 0f0e0d0c0b0a0908\n"
+ " 0000000000002010 1716151413121110\n"
+ " 0000000000002018 1f1e1d1c1b1a1918\n"
+ " 0000000000002020 2726252423222120\n"
+ " 0000000000002028 2f2e2d2c2b2a2928\n"
+ " 0000000000002030 3736353433323130\n"
+ " 0000000000002038 3f3e3d3c3b3a3938\n"
+ " 0000000000002040 4746454443424140\n"
+ " 0000000000002048 4f4e4d4c4b4a4948\n"
+ " 0000000000002050 5756555453525150\n"
+ " 0000000000002058 5f5e5d5c5b5a5958\n"
+ " 0000000000002060 6766656463626160\n"
+ " 0000000000002068 6f6e6d6c6b6a6968\n"
+ " 0000000000002070 7776757473727170\n"
+ " 0000000000002078 7f7e7d7c7b7a7978\n";
+#else
+ " 00001fc0 03020100\n"
+ " 00001fc4 07060504\n"
+ " 00001fc8 0b0a0908\n"
+ " 00001fcc 0f0e0d0c\n"
+ " 00001fd0 13121110\n"
+ " 00001fd4 17161514\n"
+ " 00001fd8 1b1a1918\n"
+ " 00001fdc 1f1e1d1c\n"
+ " 00001fe0 23222120\n"
+ " 00001fe4 27262524\n"
+ " 00001fe8 2b2a2928\n"
+ " 00001fec 2f2e2d2c\n"
+ " 00001ff0 33323130\n"
+ " 00001ff4 37363534\n"
+ " 00001ff8 3b3a3938\n"
+ " 00001ffc 3f3e3d3c\n"
+ " #00 00002000 03020100\n"
+ " 00002004 07060504\n"
+ " 00002008 0b0a0908\n"
+ " 0000200c 0f0e0d0c\n"
+ " 00002010 13121110\n"
+ " 00002014 17161514\n"
+ " 00002018 1b1a1918\n"
+ " 0000201c 1f1e1d1c\n"
+ " 00002020 23222120\n"
+ " 00002024 27262524\n"
+ " 00002028 2b2a2928\n"
+ " 0000202c 2f2e2d2c\n"
+ " 00002030 33323130\n"
+ " 00002034 37363534\n"
+ " 00002038 3b3a3938\n"
+ " 0000203c 3f3e3d3c\n";
+#endif
+ EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
+ std::vector<unwindstack::FrameData> frames;
+ unwindstack::Maps maps;
+ MemoryPattern memory;
+
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
+ dump_stack(&log_, frames, &maps, &memory);
+
+ std::string contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+ std::string expected =
+#if defined(__LP64__)
+ " 0000000000001f80 0706050403020100\n"
+ " 0000000000001f88 0f0e0d0c0b0a0908\n"
+ " 0000000000001f90 1716151413121110\n"
+ " 0000000000001f98 1f1e1d1c1b1a1918\n"
+ " 0000000000001fa0 2726252423222120\n"
+ " 0000000000001fa8 2f2e2d2c2b2a2928\n"
+ " 0000000000001fb0 3736353433323130\n"
+ " 0000000000001fb8 3f3e3d3c3b3a3938\n"
+ " 0000000000001fc0 4746454443424140\n"
+ " 0000000000001fc8 4f4e4d4c4b4a4948\n"
+ " 0000000000001fd0 5756555453525150\n"
+ " 0000000000001fd8 5f5e5d5c5b5a5958\n"
+ " 0000000000001fe0 6766656463626160\n"
+ " 0000000000001fe8 6f6e6d6c6b6a6968\n"
+ " 0000000000001ff0 7776757473727170\n"
+ " 0000000000001ff8 7f7e7d7c7b7a7978\n"
+ " #00 0000000000002000 0706050403020100\n"
+ " ................ ................\n"
+ " #01 0000000000002000 0706050403020100\n"
+ " 0000000000002008 0f0e0d0c0b0a0908\n"
+ " 0000000000002010 1716151413121110\n"
+ " 0000000000002018 1f1e1d1c1b1a1918\n"
+ " 0000000000002020 2726252423222120\n"
+ " 0000000000002028 2f2e2d2c2b2a2928\n"
+ " 0000000000002030 3736353433323130\n"
+ " 0000000000002038 3f3e3d3c3b3a3938\n"
+ " 0000000000002040 4746454443424140\n"
+ " 0000000000002048 4f4e4d4c4b4a4948\n"
+ " 0000000000002050 5756555453525150\n"
+ " 0000000000002058 5f5e5d5c5b5a5958\n"
+ " 0000000000002060 6766656463626160\n"
+ " 0000000000002068 6f6e6d6c6b6a6968\n"
+ " 0000000000002070 7776757473727170\n"
+ " 0000000000002078 7f7e7d7c7b7a7978\n";
+#else
+ " 00001fc0 03020100\n"
+ " 00001fc4 07060504\n"
+ " 00001fc8 0b0a0908\n"
+ " 00001fcc 0f0e0d0c\n"
+ " 00001fd0 13121110\n"
+ " 00001fd4 17161514\n"
+ " 00001fd8 1b1a1918\n"
+ " 00001fdc 1f1e1d1c\n"
+ " 00001fe0 23222120\n"
+ " 00001fe4 27262524\n"
+ " 00001fe8 2b2a2928\n"
+ " 00001fec 2f2e2d2c\n"
+ " 00001ff0 33323130\n"
+ " 00001ff4 37363534\n"
+ " 00001ff8 3b3a3938\n"
+ " 00001ffc 3f3e3d3c\n"
+ " #00 00002000 03020100\n"
+ " ........ ........\n"
+ " #01 00002000 03020100\n"
+ " 00002004 07060504\n"
+ " 00002008 0b0a0908\n"
+ " 0000200c 0f0e0d0c\n"
+ " 00002010 13121110\n"
+ " 00002014 17161514\n"
+ " 00002018 1b1a1918\n"
+ " 0000201c 1f1e1d1c\n"
+ " 00002020 23222120\n"
+ " 00002024 27262524\n"
+ " 00002028 2b2a2928\n"
+ " 0000202c 2f2e2d2c\n"
+ " 00002030 33323130\n"
+ " 00002034 37363534\n"
+ " 00002038 3b3a3938\n"
+ " 0000203c 3f3e3d3c\n";
+#endif
+ EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames) {
+ std::vector<unwindstack::FrameData> frames;
+ unwindstack::Maps maps;
+ MemoryPattern memory;
+
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
+ dump_stack(&log_, frames, &maps, &memory);
+
+ std::string contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+ std::string expected =
+#if defined(__LP64__)
+ " 0000000000001f80 0706050403020100\n"
+ " 0000000000001f88 0f0e0d0c0b0a0908\n"
+ " 0000000000001f90 1716151413121110\n"
+ " 0000000000001f98 1f1e1d1c1b1a1918\n"
+ " 0000000000001fa0 2726252423222120\n"
+ " 0000000000001fa8 2f2e2d2c2b2a2928\n"
+ " 0000000000001fb0 3736353433323130\n"
+ " 0000000000001fb8 3f3e3d3c3b3a3938\n"
+ " 0000000000001fc0 4746454443424140\n"
+ " 0000000000001fc8 4f4e4d4c4b4a4948\n"
+ " 0000000000001fd0 5756555453525150\n"
+ " 0000000000001fd8 5f5e5d5c5b5a5958\n"
+ " 0000000000001fe0 6766656463626160\n"
+ " 0000000000001fe8 6f6e6d6c6b6a6968\n"
+ " 0000000000001ff0 7776757473727170\n"
+ " 0000000000001ff8 7f7e7d7c7b7a7978\n"
+ " #00 0000000000002000 0706050403020100\n"
+ " 0000000000002008 0f0e0d0c0b0a0908\n"
+ " #01 0000000000002010 0706050403020100\n"
+ " 0000000000002018 0f0e0d0c0b0a0908\n"
+ " 0000000000002020 1716151413121110\n"
+ " 0000000000002028 1f1e1d1c1b1a1918\n"
+ " 0000000000002030 2726252423222120\n"
+ " 0000000000002038 2f2e2d2c2b2a2928\n"
+ " 0000000000002040 3736353433323130\n"
+ " 0000000000002048 3f3e3d3c3b3a3938\n"
+ " 0000000000002050 4746454443424140\n"
+ " 0000000000002058 4f4e4d4c4b4a4948\n"
+ " 0000000000002060 5756555453525150\n"
+ " 0000000000002068 5f5e5d5c5b5a5958\n"
+ " 0000000000002070 6766656463626160\n"
+ " 0000000000002078 6f6e6d6c6b6a6968\n"
+ " 0000000000002080 7776757473727170\n"
+ " 0000000000002088 7f7e7d7c7b7a7978\n"
+ " ................ ................\n"
+ " #02 0000000000002100 0706050403020100\n"
+ " 0000000000002108 0f0e0d0c0b0a0908\n"
+ " 0000000000002110 1716151413121110\n"
+ " 0000000000002118 1f1e1d1c1b1a1918\n"
+ " 0000000000002120 2726252423222120\n"
+ " 0000000000002128 2f2e2d2c2b2a2928\n"
+ " 0000000000002130 3736353433323130\n"
+ " 0000000000002138 3f3e3d3c3b3a3938\n"
+ " 0000000000002140 4746454443424140\n"
+ " 0000000000002148 4f4e4d4c4b4a4948\n"
+ " 0000000000002150 5756555453525150\n"
+ " 0000000000002158 5f5e5d5c5b5a5958\n"
+ " 0000000000002160 6766656463626160\n"
+ " 0000000000002168 6f6e6d6c6b6a6968\n"
+ " 0000000000002170 7776757473727170\n"
+ " 0000000000002178 7f7e7d7c7b7a7978\n";
+#else
+ " 00001fc0 03020100\n"
+ " 00001fc4 07060504\n"
+ " 00001fc8 0b0a0908\n"
+ " 00001fcc 0f0e0d0c\n"
+ " 00001fd0 13121110\n"
+ " 00001fd4 17161514\n"
+ " 00001fd8 1b1a1918\n"
+ " 00001fdc 1f1e1d1c\n"
+ " 00001fe0 23222120\n"
+ " 00001fe4 27262524\n"
+ " 00001fe8 2b2a2928\n"
+ " 00001fec 2f2e2d2c\n"
+ " 00001ff0 33323130\n"
+ " 00001ff4 37363534\n"
+ " 00001ff8 3b3a3938\n"
+ " 00001ffc 3f3e3d3c\n"
+ " #00 00002000 03020100\n"
+ " 00002004 07060504\n"
+ " 00002008 0b0a0908\n"
+ " 0000200c 0f0e0d0c\n"
+ " #01 00002010 03020100\n"
+ " 00002014 07060504\n"
+ " 00002018 0b0a0908\n"
+ " 0000201c 0f0e0d0c\n"
+ " 00002020 13121110\n"
+ " 00002024 17161514\n"
+ " 00002028 1b1a1918\n"
+ " 0000202c 1f1e1d1c\n"
+ " 00002030 23222120\n"
+ " 00002034 27262524\n"
+ " 00002038 2b2a2928\n"
+ " 0000203c 2f2e2d2c\n"
+ " 00002040 33323130\n"
+ " 00002044 37363534\n"
+ " 00002048 3b3a3938\n"
+ " 0000204c 3f3e3d3c\n"
+ " ........ ........\n"
+ " #02 00002100 03020100\n"
+ " 00002104 07060504\n"
+ " 00002108 0b0a0908\n"
+ " 0000210c 0f0e0d0c\n"
+ " 00002110 13121110\n"
+ " 00002114 17161514\n"
+ " 00002118 1b1a1918\n"
+ " 0000211c 1f1e1d1c\n"
+ " 00002120 23222120\n"
+ " 00002124 27262524\n"
+ " 00002128 2b2a2928\n"
+ " 0000212c 2f2e2d2c\n"
+ " 00002130 33323130\n"
+ " 00002134 37363534\n"
+ " 00002138 3b3a3938\n"
+ " 0000213c 3f3e3d3c\n";
+#endif
+ EXPECT_EQ(expected, contents);
+}
+
+TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
+ std::vector<unwindstack::FrameData> frames;
+ unwindstack::Maps maps;
+ MemoryPattern memory;
+
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
+ frames.push_back(
+ unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
+ dump_stack(&log_, frames, &maps, &memory);
+
+ std::string contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
+
+ std::string expected =
+#if defined(__LP64__)
+ " 0000000000001f80 0706050403020100\n"
+ " 0000000000001f88 0f0e0d0c0b0a0908\n"
+ " 0000000000001f90 1716151413121110\n"
+ " 0000000000001f98 1f1e1d1c1b1a1918\n"
+ " 0000000000001fa0 2726252423222120\n"
+ " 0000000000001fa8 2f2e2d2c2b2a2928\n"
+ " 0000000000001fb0 3736353433323130\n"
+ " 0000000000001fb8 3f3e3d3c3b3a3938\n"
+ " 0000000000001fc0 4746454443424140\n"
+ " 0000000000001fc8 4f4e4d4c4b4a4948\n"
+ " 0000000000001fd0 5756555453525150\n"
+ " 0000000000001fd8 5f5e5d5c5b5a5958\n"
+ " 0000000000001fe0 6766656463626160\n"
+ " 0000000000001fe8 6f6e6d6c6b6a6968\n"
+ " 0000000000001ff0 7776757473727170\n"
+ " 0000000000001ff8 7f7e7d7c7b7a7978\n"
+ " #00 0000000000002000 0706050403020100\n"
+ " 0000000000002008 0f0e0d0c0b0a0908\n"
+ " #01 0000000000002010 0706050403020100\n"
+ " 0000000000002018 0f0e0d0c0b0a0908\n"
+ " 0000000000002020 1716151413121110\n"
+ " 0000000000002028 1f1e1d1c1b1a1918\n"
+ " 0000000000002030 2726252423222120\n"
+ " 0000000000002038 2f2e2d2c2b2a2928\n"
+ " 0000000000002040 3736353433323130\n"
+ " 0000000000002048 3f3e3d3c3b3a3938\n"
+ " 0000000000002050 4746454443424140\n"
+ " 0000000000002058 4f4e4d4c4b4a4948\n"
+ " 0000000000002060 5756555453525150\n"
+ " 0000000000002068 5f5e5d5c5b5a5958\n"
+ " 0000000000002070 6766656463626160\n"
+ " 0000000000002078 6f6e6d6c6b6a6968\n"
+ " 0000000000002080 7776757473727170\n"
+ " 0000000000002088 7f7e7d7c7b7a7978\n"
+ " ................ ................\n"
+ " #02 0000000000001000 0706050403020100\n"
+ " 0000000000001008 0f0e0d0c0b0a0908\n"
+ " 0000000000001010 1716151413121110\n"
+ " 0000000000001018 1f1e1d1c1b1a1918\n"
+ " 0000000000001020 2726252423222120\n"
+ " 0000000000001028 2f2e2d2c2b2a2928\n"
+ " #03 0000000000001030 0706050403020100\n"
+ " 0000000000001038 0f0e0d0c0b0a0908\n"
+ " 0000000000001040 1716151413121110\n"
+ " 0000000000001048 1f1e1d1c1b1a1918\n"
+ " 0000000000001050 2726252423222120\n"
+ " 0000000000001058 2f2e2d2c2b2a2928\n"
+ " 0000000000001060 3736353433323130\n"
+ " 0000000000001068 3f3e3d3c3b3a3938\n"
+ " 0000000000001070 4746454443424140\n"
+ " 0000000000001078 4f4e4d4c4b4a4948\n"
+ " 0000000000001080 5756555453525150\n"
+ " 0000000000001088 5f5e5d5c5b5a5958\n"
+ " 0000000000001090 6766656463626160\n"
+ " 0000000000001098 6f6e6d6c6b6a6968\n"
+ " 00000000000010a0 7776757473727170\n"
+ " 00000000000010a8 7f7e7d7c7b7a7978\n";
+#else
+ " 00001fc0 03020100\n"
+ " 00001fc4 07060504\n"
+ " 00001fc8 0b0a0908\n"
+ " 00001fcc 0f0e0d0c\n"
+ " 00001fd0 13121110\n"
+ " 00001fd4 17161514\n"
+ " 00001fd8 1b1a1918\n"
+ " 00001fdc 1f1e1d1c\n"
+ " 00001fe0 23222120\n"
+ " 00001fe4 27262524\n"
+ " 00001fe8 2b2a2928\n"
+ " 00001fec 2f2e2d2c\n"
+ " 00001ff0 33323130\n"
+ " 00001ff4 37363534\n"
+ " 00001ff8 3b3a3938\n"
+ " 00001ffc 3f3e3d3c\n"
+ " #00 00002000 03020100\n"
+ " 00002004 07060504\n"
+ " 00002008 0b0a0908\n"
+ " 0000200c 0f0e0d0c\n"
+ " #01 00002010 03020100\n"
+ " 00002014 07060504\n"
+ " 00002018 0b0a0908\n"
+ " 0000201c 0f0e0d0c\n"
+ " 00002020 13121110\n"
+ " 00002024 17161514\n"
+ " 00002028 1b1a1918\n"
+ " 0000202c 1f1e1d1c\n"
+ " 00002030 23222120\n"
+ " 00002034 27262524\n"
+ " 00002038 2b2a2928\n"
+ " 0000203c 2f2e2d2c\n"
+ " 00002040 33323130\n"
+ " 00002044 37363534\n"
+ " 00002048 3b3a3938\n"
+ " 0000204c 3f3e3d3c\n"
+ " ........ ........\n"
+ " #02 00001000 03020100\n"
+ " 00001004 07060504\n"
+ " 00001008 0b0a0908\n"
+ " 0000100c 0f0e0d0c\n"
+ " 00001010 13121110\n"
+ " 00001014 17161514\n"
+ " 00001018 1b1a1918\n"
+ " 0000101c 1f1e1d1c\n"
+ " 00001020 23222120\n"
+ " 00001024 27262524\n"
+ " 00001028 2b2a2928\n"
+ " 0000102c 2f2e2d2c\n"
+ " #03 00001030 03020100\n"
+ " 00001034 07060504\n"
+ " 00001038 0b0a0908\n"
+ " 0000103c 0f0e0d0c\n"
+ " 00001040 13121110\n"
+ " 00001044 17161514\n"
+ " 00001048 1b1a1918\n"
+ " 0000104c 1f1e1d1c\n"
+ " 00001050 23222120\n"
+ " 00001054 27262524\n"
+ " 00001058 2b2a2928\n"
+ " 0000105c 2f2e2d2c\n"
+ " 00001060 33323130\n"
+ " 00001064 37363534\n"
+ " 00001068 3b3a3938\n"
+ " 0000106c 3f3e3d3c\n";
+#endif
+ EXPECT_EQ(expected, contents);
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 0b8a936..da2ba58 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <time.h>
@@ -41,19 +42,20 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/log.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
// Needed to get DEBUGGER_SIGNAL.
#include "debuggerd/handler.h"
#include "libdebuggerd/backtrace.h"
-#include "libdebuggerd/elf_utils.h"
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
@@ -62,9 +64,6 @@
using android::base::StringPrintf;
using android::base::unique_fd;
-using unwindstack::Memory;
-using unwindstack::Regs;
-
using namespace std::literals::string_literals;
#define STACK_WORDS 16
@@ -78,7 +77,48 @@
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
-static void dump_probable_cause(log_t* log, const siginfo_t* si) {
+static void dump_timestamp(log_t* log, time_t time) {
+ struct tm tm;
+ localtime_r(&time, &tm);
+
+ char buf[strlen("1970-01-01 00:00:00+0830") + 1];
+ strftime(buf, sizeof(buf), "%F %T%z", &tm);
+ _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
+}
+
+static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
+ unwindstack::Maps* maps) {
+ static constexpr uint64_t kMaxDifferenceBytes = 256;
+ uint64_t difference;
+ if (sp >= fault_addr) {
+ difference = sp - fault_addr;
+ } else {
+ difference = fault_addr - sp;
+ }
+ if (difference <= kMaxDifferenceBytes) {
+ // The faulting address is close to the current sp, check if the sp
+ // indicates a stack overflow.
+ // On arm, the sp does not get updated when the instruction faults.
+ // In this case, the sp will still be in a valid map, which is the
+ // last case below.
+ // On aarch64, the sp does get updated when the instruction faults.
+ // In this case, the sp will be in either an invalid map if triggered
+ // on the main thread, or in a guard map if in another thread, which
+ // will be the first case or second case from below.
+ unwindstack::MapInfo* map_info = maps->Find(sp);
+ if (map_info == nullptr) {
+ return "stack pointer is in a non-existent map; likely due to stack overflow.";
+ } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+ return "stack pointer is not in a rw map; likely due to stack overflow.";
+ } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
+ return "stack pointer is close to top of stack; likely stack overflow.";
+ }
+ }
+ return "";
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
+ unwindstack::Regs* regs) {
std::string cause;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -93,6 +133,16 @@
cause = "call to kuser_memory_barrier";
} else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
cause = "call to kuser_cmpxchg64";
+ } else {
+ cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
+ }
+ } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
+ uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
+ unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+ if (map_info != nullptr && map_info->flags == PROT_EXEC) {
+ cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+ } else {
+ cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
}
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -102,7 +152,8 @@
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
}
-static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
+ unwindstack::Memory* process_memory) {
char addr_desc[64]; // ", fault addr 0x1234"
if (signal_has_si_addr(thread_info.siginfo)) {
void* addr = thread_info.siginfo->si_addr;
@@ -125,8 +176,6 @@
_LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
-
- dump_probable_cause(log, thread_info.siginfo);
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
@@ -139,16 +188,17 @@
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
+ _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
}
-static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
+static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
uint64_t* sp, size_t words, int label) {
// Read the data all at once.
word_t stack_data[words];
// TODO: Do we need to word align this for crashes caused by a misaligned sp?
// The process_vm_readv implementation of Memory should handle this appropriately?
- size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words);
+ size_t bytes_read = memory->Read(*sp, stack_data, sizeof(word_t) * words);
words = bytes_read / sizeof(word_t);
std::string line;
for (size_t i = 0; i < words; i++) {
@@ -161,17 +211,15 @@
}
line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
- backtrace_map_t map;
- backtrace_map->FillIn(stack_data[i], &map);
- std::string map_name{map.Name()};
- if (BacktraceMap::IsValid(map) && !map_name.empty()) {
- line += " " + map_name;
- uint64_t offset = 0;
- std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
- if (!func_name.empty()) {
+ unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
+ if (map_info != nullptr && !map_info->name.empty()) {
+ line += " " + map_info->name;
+ std::string func_name;
+ uint64_t func_offset = 0;
+ if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
line += " (" + func_name;
- if (offset) {
- line += StringPrintf("+%" PRIu64, offset);
+ if (func_offset) {
+ line += StringPrintf("+%" PRIu64, func_offset);
}
line += ')';
}
@@ -182,12 +230,11 @@
}
}
-static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
- std::vector<backtrace_frame_data_t>& frames) {
+static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
+ unwindstack::Maps* maps, unwindstack::Memory* memory) {
size_t first = 0, last;
for (size_t i = 0; i < frames.size(); i++) {
- const backtrace_frame_data_t& frame = frames[i];
- if (frame.sp) {
+ if (frames[i].sp) {
if (!first) {
first = i+1;
}
@@ -202,29 +249,44 @@
// Dump a few words before the first frame.
uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
- dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
+ dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
+
+#if defined(__LP64__)
+ static constexpr const char delimiter[] = " ................ ................\n";
+#else
+ static constexpr const char delimiter[] = " ........ ........\n";
+#endif
// Dump a few words from all successive frames.
- // Only log the first 3 frames, put the rest in the tombstone.
for (size_t i = first; i <= last; i++) {
- const backtrace_frame_data_t* frame = &frames[i];
+ auto* frame = &frames[i];
if (sp != frame->sp) {
- _LOG(log, logtype::STACK, " ........ ........\n");
+ _LOG(log, logtype::STACK, delimiter);
sp = frame->sp;
}
- if (i == last) {
- dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
- if (sp < frame->sp + frame->stack_size) {
- _LOG(log, logtype::STACK, " ........ ........\n");
- }
- } else {
- size_t words = frame->stack_size / sizeof(word_t);
- if (words == 0) {
- words = 1;
- } else if (words > STACK_WORDS) {
+ if (i != last) {
+ // Print stack data up to the stack from the next frame.
+ size_t words;
+ uint64_t next_sp = frames[i + 1].sp;
+ if (next_sp < sp) {
+ // The next frame is probably using a completely different stack,
+ // so dump the max from this stack.
words = STACK_WORDS;
+ } else {
+ words = (next_sp - sp) / sizeof(word_t);
+ if (words == 0) {
+ // The sp is the same as the next frame, print at least
+ // one line for this frame.
+ words = 1;
+ } else if (words > STACK_WORDS) {
+ words = STACK_WORDS;
+ }
}
- dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
+ dump_stack_segment(log, maps, memory, &sp, words, i);
+ } else {
+ // Print some number of words past the last stack frame since we
+ // don't know how large the stack is.
+ dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
}
}
}
@@ -241,7 +303,7 @@
return addr_str;
}
-static void dump_abort_message(log_t* log, Memory* process_memory, uint64_t address) {
+static void dump_abort_message(log_t* log, unwindstack::Memory* process_memory, uint64_t address) {
if (address == 0) {
return;
}
@@ -270,16 +332,16 @@
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
}
-static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uint64_t addr) {
+static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) {
bool print_fault_address_marker = addr;
- ScopedBacktraceMapIteratorLock lock(map);
+ unwindstack::Maps* maps = unwinder->GetMaps();
_LOG(log, logtype::MAPS,
"\n"
"memory map (%zu entr%s):",
- map->size(), map->size() == 1 ? "y" : "ies");
+ maps->Total(), maps->Total() == 1 ? "y" : "ies");
if (print_fault_address_marker) {
- if (map->begin() != map->end() && addr < (*map->begin())->start) {
+ if (maps->Total() != 0 && addr < maps->Get(0)->start) {
_LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
@@ -290,51 +352,54 @@
_LOG(log, logtype::MAPS, "\n");
}
+ std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
+
std::string line;
- for (auto it = map->begin(); it != map->end(); ++it) {
- const backtrace_map_t* entry = *it;
+ for (auto const& map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
- if (addr < entry->start) {
+ if (addr < map_info->start) {
_LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
- } else if (addr >= entry->start && addr < entry->end) {
+ } else if (addr >= map_info->start && addr < map_info->end) {
line = "--->";
print_fault_address_marker = false;
}
}
- line += get_addr_string(entry->start) + '-' + get_addr_string(entry->end - 1) + ' ';
- if (entry->flags & PROT_READ) {
+ line += get_addr_string(map_info->start) + '-' + get_addr_string(map_info->end - 1) + ' ';
+ if (map_info->flags & PROT_READ) {
line += 'r';
} else {
line += '-';
}
- if (entry->flags & PROT_WRITE) {
+ if (map_info->flags & PROT_WRITE) {
line += 'w';
} else {
line += '-';
}
- if (entry->flags & PROT_EXEC) {
+ if (map_info->flags & PROT_EXEC) {
line += 'x';
} else {
line += '-';
}
- line += StringPrintf(" %8" PRIx64 " %8" PRIx64, entry->offset, entry->end - entry->start);
+ line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset,
+ map_info->end - map_info->start);
bool space_needed = true;
- if (entry->name.length() > 0) {
+ if (!map_info->name.empty()) {
space_needed = false;
- line += " " + entry->name;
- std::string build_id;
- if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
+ line += " " + map_info->name;
+ std::string build_id = map_info->GetPrintableBuildID();
+ if (!build_id.empty()) {
line += " (BuildId: " + build_id + ")";
}
}
- if (entry->load_bias != 0) {
+ uint64_t load_bias = map_info->GetLoadBias(process_memory);
+ if (load_bias != 0) {
if (space_needed) {
line += ' ';
}
- line += StringPrintf(" (load bias 0x%" PRIx64 ")", entry->load_bias);
+ line += StringPrintf(" (load bias 0x%" PRIx64 ")", load_bias);
}
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
@@ -344,12 +409,6 @@
}
}
-void dump_backtrace(log_t* log, std::vector<backtrace_frame_data_t>& frames, const char* prefix) {
- for (auto& frame : frames) {
- _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str());
- }
-}
-
static void print_register_row(log_t* log,
const std::vector<std::pair<std::string, uint64_t>>& registers) {
std::string output;
@@ -362,7 +421,7 @@
_LOG(log, logtype::REGISTERS, " %s\n", output.c_str());
}
-void dump_registers(log_t* log, Regs* regs) {
+void dump_registers(log_t* log, unwindstack::Regs* regs) {
// Split lr/sp/pc into their own special row.
static constexpr size_t column_count = 4;
std::vector<std::pair<std::string, uint64_t>> current_row;
@@ -401,23 +460,22 @@
print_register_row(log, special_row);
}
-void dump_memory_and_code(log_t* log, BacktraceMap* map, Memory* memory, Regs* regs) {
- regs->IterateRegisters([log, map, memory](const char* reg_name, uint64_t reg_value) {
+void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
+ unwindstack::Regs* regs) {
+ regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
std::string label{"memory near "s + reg_name};
- if (map) {
- backtrace_map_t map_info;
- map->FillIn(reg_value, &map_info);
- std::string map_name{map_info.Name()};
- if (!map_name.empty()) label += " (" + map_info.Name() + ")";
+ if (maps) {
+ unwindstack::MapInfo* map_info = maps->Find(reg_value);
+ if (map_info != nullptr && !map_info->name.empty()) {
+ label += " (" + map_info->name + ")";
+ }
}
dump_memory(log, memory, reg_value, label);
});
}
-static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
- const ThreadInfo& thread_info, uint64_t abort_msg_address,
- bool primary_thread) {
- UNUSED(process_memory);
+static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
+ uint64_t abort_msg_address, bool primary_thread) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@@ -425,40 +483,41 @@
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info, process_memory);
+ dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
+ dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
}
if (primary_thread) {
- dump_abort_message(log, process_memory, abort_msg_address);
+ dump_abort_message(log, unwinder->GetProcessMemory().get(), abort_msg_address);
}
dump_registers(log, thread_info.registers.get());
// Unwind will mutate the registers, so make a copy first.
- std::unique_ptr<Regs> regs_copy(thread_info.registers->Clone());
- std::vector<backtrace_frame_data_t> frames;
- if (!Backtrace::Unwind(regs_copy.get(), map, &frames, 0, nullptr)) {
+ std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+ unwinder->SetRegs(regs_copy.get());
+ unwinder->Unwind();
+ if (unwinder->NumFrames() == 0) {
_LOG(log, logtype::THREAD, "Failed to unwind");
- return false;
- }
-
- if (!frames.empty()) {
+ } else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
- dump_backtrace(log, frames, " ");
+ log_backtrace(log, unwinder, " ");
_LOG(log, logtype::STACK, "\nstack:\n");
- dump_stack(log, map, process_memory, frames);
+ dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
}
if (primary_thread) {
- dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
- if (map) {
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
+ thread_info.registers.get());
+ if (maps != nullptr) {
uint64_t addr = 0;
siginfo_t* si = thread_info.siginfo;
if (signal_has_si_addr(si)) {
addr = reinterpret_cast<uint64_t>(si->si_addr);
}
- dump_all_maps(log, map, process_memory, addr);
+ dump_all_maps(log, unwinder, addr);
}
}
@@ -594,6 +653,7 @@
void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext) {
+ pid_t uid = getuid();
pid_t pid = getpid();
pid_t tid = gettid();
@@ -609,11 +669,13 @@
read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
- std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
+ std::unique_ptr<unwindstack::Regs> regs(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
std::map<pid_t, ThreadInfo> threads;
threads[gettid()] = ThreadInfo{
.registers = std::move(regs),
+ .uid = uid,
.tid = tid,
.thread_name = thread_name,
.pid = pid,
@@ -621,18 +683,16 @@
.siginfo = siginfo,
};
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
- if (!backtrace_map) {
- ALOGE("failed to create backtrace map");
- _exit(1);
+ unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
+ if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ LOG(FATAL) << "Failed to init unwinder object.";
}
- std::shared_ptr<Memory> process_memory = backtrace_map->GetProcessMemory();
- engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(),
- threads, tid, abort_msg_address, nullptr, nullptr);
+ engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
+ nullptr, nullptr);
}
-void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
+void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
std::string* amfd_data) {
@@ -647,12 +707,13 @@
_LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
dump_header_info(&log);
+ dump_timestamp(&log, time(nullptr));
auto it = threads.find(target_thread);
if (it == threads.end()) {
LOG(FATAL) << "failed to find target thread";
}
- dump_thread(&log, map, process_memory, it->second, abort_msg_address, true);
+ dump_thread(&log, unwinder, it->second, abort_msg_address, true);
if (want_logs) {
dump_logs(&log, it->second.pid, 50);
@@ -663,7 +724,7 @@
continue;
}
- dump_thread(&log, map, process_memory, thread_info, 0, false);
+ dump_thread(&log, unwinder, thread_info, 0, false);
}
if (open_files) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d0c5234..9b2779a 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,10 +35,10 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
#include <debuggerd/handler.h>
#include <log/log.h>
#include <unwindstack/Memory.h>
+#include <unwindstack/Unwinder.h>
using android::base::unique_fd;
@@ -423,3 +423,22 @@
// Then give up...
return "?";
}
+
+void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+ if (unwinder->elf_from_memory_not_file()) {
+ _LOG(log, logtype::BACKTRACE,
+ "%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
+ _LOG(log, logtype::BACKTRACE,
+ "%sNOTE: to unreadable libraries. For unwinds of apps, only shared libraries\n", prefix);
+ _LOG(log, logtype::BACKTRACE, "%sNOTE: found under the lib/ directory are readable.\n", prefix);
+#if defined(ROOT_POSSIBLE)
+ _LOG(log, logtype::BACKTRACE,
+ "%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
+#endif
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
+ }
+}
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index c446dbb..7d25c50 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -24,6 +24,7 @@
#include <event2/event.h>
#include <event2/listener.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@@ -31,6 +32,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
@@ -96,7 +98,8 @@
{
unique_fd rcv_fd;
InterceptRequest intercept_request;
- ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+ ssize_t result =
+ ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
if (result == -1) {
PLOG(WARNING) << "failed to read from intercept socket";
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index ad92067..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -31,6 +31,7 @@
#include <event2/listener.h>
#include <event2/thread.h>
+#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -45,6 +46,7 @@
#include "intercept_manager.h"
using android::base::GetIntProperty;
+using android::base::SendFileDescriptors;
using android::base::StringPrintf;
using android::base::unique_fd;
@@ -224,7 +226,10 @@
TombstonedCrashPacket response = {
.packet_type = CrashPacketType::kPerformDump
};
- ssize_t rc = send_fd(crash->crash_socket_fd, &response, sizeof(response), std::move(output_fd));
+ ssize_t rc =
+ SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());
+ output_fd.reset();
+
if (rc == -1) {
PLOG(WARNING) << "failed to send response to CrashRequest";
goto fail;
diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp
index bdb4c1a..2c23c98 100644
--- a/debuggerd/tombstoned/tombstoned_client.cpp
+++ b/debuggerd/tombstoned/tombstoned_client.cpp
@@ -21,6 +21,7 @@
#include <utility>
+#include <android-base/cmsg.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <cutils/sockets.h>
@@ -28,6 +29,7 @@
#include "protocol.h"
#include "util.h"
+using android::base::ReceiveFileDescriptors;
using android::base::unique_fd;
bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
@@ -53,7 +55,7 @@
}
unique_fd tmp_output_fd;
- ssize_t rc = recv_fd(sockfd, &packet, sizeof(packet), &tmp_output_fd);
+ ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
if (rc == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"failed to read response to DumpRequest packet: %s", strerror(errno));
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 50c5efc..a37b3b9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -24,73 +24,9 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include "protocol.h"
-using android::base::unique_fd;
-
-ssize_t send_fd(int sockfd, const void* data, size_t len, unique_fd fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf),
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd.get();
-
- return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, 0));
-}
-
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullable out_fd) {
- char cmsg_buf[CMSG_SPACE(sizeof(int))];
-
- iovec iov = { .iov_base = const_cast<void*>(data), .iov_len = len };
- msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = cmsg_buf,
- .msg_controllen = sizeof(cmsg_buf),
- .msg_flags = 0,
- };
- auto cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-
- ssize_t result = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, 0));
- if (result == -1) {
- return -1;
- }
-
- unique_fd fd;
- bool received_fd = msg.msg_controllen == sizeof(cmsg_buf);
- if (received_fd) {
- fd.reset(*reinterpret_cast<int*>(CMSG_DATA(cmsg)));
- }
-
- if ((msg.msg_flags & MSG_TRUNC) != 0) {
- errno = EFBIG;
- return -1;
- } else if ((msg.msg_flags & MSG_CTRUNC) != 0) {
- errno = ERANGE;
- return -1;
- }
-
- if (out_fd) {
- *out_fd = std::move(fd);
- } else if (received_fd) {
- errno = ERANGE;
- return -1;
- }
-
- return result;
-}
-
std::string get_process_name(pid_t pid) {
std::string result = "<unknown>";
android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result);
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 8260b44..e964423 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -21,29 +21,5 @@
#include <sys/cdefs.h>
#include <sys/types.h>
-#include <android-base/unique_fd.h>
-
-// *** WARNING ***
-// tombstoned's sockets are SOCK_SEQPACKET sockets.
-// Short reads are treated as errors and short writes are assumed to not happen.
-
-// Sends a packet with an attached fd.
-ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base::unique_fd fd);
-
-// Receives a packet and optionally, its attached fd.
-// If out_fd is non-null, packets can optionally have an attached fd.
-// If out_fd is null, received packets must not have an attached fd.
-//
-// Errors:
-// EOVERFLOW: sockfd is SOCK_DGRAM or SOCK_SEQPACKET and buffer is too small.
-// The first len bytes of the packet are stored in data, but the
-// rest of the packet is dropped.
-// ERANGE: too many file descriptors were attached to the packet.
-// ENOMSG: not enough file descriptors were attached to the packet.
-//
-// plus any errors returned by the underlying recvmsg.
-ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len,
- android::base::unique_fd* _Nullable out_fd);
-
std::string get_process_name(pid_t pid);
std::string get_thread_name(pid_t tid);
diff --git a/demangle/Android.bp b/demangle/Android.bp
deleted file mode 100644
index fd79cf8..0000000
--- a/demangle/Android.bp
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_defaults {
- name: "libdemangle_defaults",
-
- host_supported: true,
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- target: {
- linux_bionic: {
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libdemangle",
- defaults: ["libdemangle_defaults"],
- vendor_available: true,
- recovery_available: true,
-
- srcs: [
- "Demangler.cpp",
- ],
-
- local_include_dirs: [
- "include",
- ],
-
- export_include_dirs: [
- "include",
- ],
-}
-
-cc_binary {
- name: "demangle",
- defaults: ["libdemangle_defaults"],
- srcs: ["demangle.cpp"],
- host_supported: true,
-
- shared_libs: ["libdemangle"],
-}
-
-//-------------------------------------------------------------------------
-// Unit Tests
-//-------------------------------------------------------------------------
-cc_test {
- name: "libdemangle_test",
- defaults: ["libdemangle_defaults"],
-
- srcs: [
- "DemangleTest.cpp",
- ],
-
- cflags: [
- "-O0",
- "-g",
- ],
-
- shared_libs: [
- "libdemangle",
- ],
-
- test_suites: ["device-tests"],
- required: [
- "libdemangle",
- ],
-}
diff --git a/demangle/Android.mk b/demangle/Android.mk
deleted file mode 100644
index d8082a9..0000000
--- a/demangle/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := demangle_fuzzer
-LOCAL_SRC_FILES := \
- Demangler.cpp \
- demangle_fuzzer.cpp \
-
-LOCAL_CFLAGS := \
- -Wall \
- -Werror \
- -Wextra \
-
-include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
deleted file mode 100644
index 1787031..0000000
--- a/demangle/DemangleTest.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <gtest/gtest.h>
-
-#include <demangle.h>
-
-#include "Demangler.h"
-
-TEST(DemangleTest, IllegalArgumentModifiers) {
- Demangler demangler;
-
- ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
- ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
-}
-
-TEST(DemangleTest, VoidArgument) {
- Demangler demangler;
-
- ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
- ASSERT_EQ("func(void&)", demangler.Parse("_ZN4funcERv"));
- ASSERT_EQ("func(void, void)", demangler.Parse("_ZN4funcEvv"));
- ASSERT_EQ("func(void*)", demangler.Parse("_ZN4funcEPv"));
- ASSERT_EQ("func(void const)", demangler.Parse("_ZN4funcEKv"));
- ASSERT_EQ("func(void volatile)", demangler.Parse("_ZN4funcEVv"));
-}
-
-TEST(DemangleTest, ArgumentModifiers) {
- Demangler demangler;
-
- ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
- ASSERT_EQ("func(char*)", demangler.Parse("_ZN4funcEPc"));
- ASSERT_EQ("func(char**)", demangler.Parse("_ZN4funcEPPc"));
- ASSERT_EQ("func(char***)", demangler.Parse("_ZN4funcEPPPc"));
- ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERc"));
- ASSERT_EQ("func(char*&)", demangler.Parse("_ZN4funcERPc"));
- ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERRc"));
- ASSERT_EQ("func(char*&*)", demangler.Parse("_ZN4funcEPRPc"));
- ASSERT_EQ("func(char**&)", demangler.Parse("_ZN4funcERRPPc"));
- ASSERT_EQ("func(char const)", demangler.Parse("_ZN4funcEKc"));
- ASSERT_EQ("func(char volatile)", demangler.Parse("_ZN4funcEVc"));
- ASSERT_EQ("func(char volatile const)", demangler.Parse("_ZN4funcEKVc"));
- ASSERT_EQ("func(char const volatile)", demangler.Parse("_ZN4funcEVKc"));
- ASSERT_EQ("func(char const* volatile&)", demangler.Parse("_ZN4funcERVPKc"));
- ASSERT_EQ("func(void, char, short)", demangler.Parse("_ZN4funcEvcs"));
- ASSERT_EQ("func(void*, char&, short&*)", demangler.Parse("_ZN4funcEPvRcPRs"));
-}
-
-TEST(DemangleTest, FunctionModifiers) {
- Demangler demangler;
-
- ASSERT_EQ("func() const", demangler.Parse("_ZNK4funcEv"));
- ASSERT_EQ("func() volatile", demangler.Parse("_ZNV4funcEv"));
- ASSERT_EQ("func() volatile const", demangler.Parse("_ZNKV4funcEv"));
- ASSERT_EQ("func() const volatile", demangler.Parse("_ZNVK4funcEv"));
-}
-
-TEST(DemangleTest, MultiplePartsInName) {
- Demangler demangler;
-
- ASSERT_EQ("one::two()", demangler.Parse("_ZN3one3twoEv"));
- ASSERT_EQ("one::two::three()", demangler.Parse("_ZN3one3two5threeEv"));
- ASSERT_EQ("one::two::three::four()", demangler.Parse("_ZN3one3two5three4fourEv"));
- ASSERT_EQ("one::two::three::four::five()", demangler.Parse("_ZN3one3two5three4four4fiveEv"));
- ASSERT_EQ("one(two::three::four::five)", demangler.Parse("_ZN3oneEN3two5three4four4fiveE"));
-}
-
-TEST(DemangleTest, AnonymousNamespace) {
- Demangler demangler;
-
- ASSERT_EQ("(anonymous namespace)::two()", demangler.Parse("_ZN12_GLOBAL__N_13twoEv"));
- ASSERT_EQ("one::two((anonymous namespace))", demangler.Parse("_ZN3one3twoE12_GLOBAL__N_1"));
-}
-
-TEST(DemangleTest, DestructorValues) {
- Demangler demangler;
-
- ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD0Ev"));
- ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD1Ev"));
- ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD2Ev"));
- ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD5Ev"));
- ASSERT_EQ("one::two::three::~three()", demangler.Parse("_ZN3one3two5threeD0Ev"));
-
- ASSERT_EQ("_ZN3one3twoD3Ev", demangler.Parse("_ZN3one3twoD3Ev"));
- ASSERT_EQ("_ZN3one3twoD4Ev", demangler.Parse("_ZN3one3twoD4Ev"));
- ASSERT_EQ("_ZN3one3twoD6Ev", demangler.Parse("_ZN3one3twoD6Ev"));
- ASSERT_EQ("_ZN3one3twoD7Ev", demangler.Parse("_ZN3one3twoD7Ev"));
- ASSERT_EQ("_ZN3one3twoD8Ev", demangler.Parse("_ZN3one3twoD8Ev"));
- ASSERT_EQ("_ZN3one3twoD9Ev", demangler.Parse("_ZN3one3twoD9Ev"));
-
- ASSERT_EQ("one::two<three::four>::~two()", demangler.Parse("_ZN3one3twoIN5three4fourEED2Ev"));
-}
-
-TEST(DemangleTest, ConstructorValues) {
- Demangler demangler;
-
- ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC1Ev"));
- ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC2Ev"));
- ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC3Ev"));
- ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC5Ev"));
- ASSERT_EQ("one::two::three::three()", demangler.Parse("_ZN3one3two5threeC1Ev"));
-
- ASSERT_EQ("_ZN3one3twoC0Ev", demangler.Parse("_ZN3one3twoC0Ev"));
- ASSERT_EQ("_ZN3one3twoC4Ev", demangler.Parse("_ZN3one3twoC4Ev"));
- ASSERT_EQ("_ZN3one3twoC6Ev", demangler.Parse("_ZN3one3twoC6Ev"));
- ASSERT_EQ("_ZN3one3twoC7Ev", demangler.Parse("_ZN3one3twoC7Ev"));
- ASSERT_EQ("_ZN3one3twoC8Ev", demangler.Parse("_ZN3one3twoC8Ev"));
- ASSERT_EQ("_ZN3one3twoC9Ev", demangler.Parse("_ZN3one3twoC9Ev"));
-
- ASSERT_EQ("one::two<three::four>::two()", demangler.Parse("_ZN3one3twoIN5three4fourEEC1Ev"));
-}
-
-TEST(DemangleTest, OperatorValues) {
- Demangler demangler;
-
- ASSERT_EQ("operator&&()", demangler.Parse("_Zaav"));
- ASSERT_EQ("operator&()", demangler.Parse("_Zadv"));
- ASSERT_EQ("operator&()", demangler.Parse("_Zanv"));
- ASSERT_EQ("operator&=()", demangler.Parse("_ZaNv"));
- ASSERT_EQ("operator=()", demangler.Parse("_ZaSv"));
- ASSERT_EQ("operator()()", demangler.Parse("_Zclv"));
- ASSERT_EQ("operator,()", demangler.Parse("_Zcmv"));
- ASSERT_EQ("operator~()", demangler.Parse("_Zcov"));
- ASSERT_EQ("operator delete[]()", demangler.Parse("_Zdav"));
- ASSERT_EQ("operator*()", demangler.Parse("_Zdev"));
- ASSERT_EQ("operator delete()", demangler.Parse("_Zdlv"));
- ASSERT_EQ("operator/()", demangler.Parse("_Zdvv"));
- ASSERT_EQ("operator/=()", demangler.Parse("_ZdVv"));
- ASSERT_EQ("operator^()", demangler.Parse("_Zeov"));
- ASSERT_EQ("operator^=()", demangler.Parse("_ZeOv"));
- ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
- ASSERT_EQ("operator>=()", demangler.Parse("_Zgev"));
- ASSERT_EQ("operator>()", demangler.Parse("_Zgtv"));
- ASSERT_EQ("operator[]()", demangler.Parse("_Zixv"));
- ASSERT_EQ("operator<=()", demangler.Parse("_Zlev"));
- ASSERT_EQ("operator<<()", demangler.Parse("_Zlsv"));
- ASSERT_EQ("operator<<=()", demangler.Parse("_ZlSv"));
- ASSERT_EQ("operator<()", demangler.Parse("_Zltv"));
- ASSERT_EQ("operator-()", demangler.Parse("_Zmiv"));
- ASSERT_EQ("operator-=()", demangler.Parse("_ZmIv"));
- ASSERT_EQ("operator*()", demangler.Parse("_Zmlv"));
- ASSERT_EQ("operator*=()", demangler.Parse("_ZmLv"));
- ASSERT_EQ("operator--()", demangler.Parse("_Zmmv"));
- ASSERT_EQ("operator new[]()", demangler.Parse("_Znav"));
- ASSERT_EQ("operator!=()", demangler.Parse("_Znev"));
- ASSERT_EQ("operator-()", demangler.Parse("_Zngv"));
- ASSERT_EQ("operator!()", demangler.Parse("_Zntv"));
- ASSERT_EQ("operator new()", demangler.Parse("_Znwv"));
- ASSERT_EQ("operator||()", demangler.Parse("_Zoov"));
- ASSERT_EQ("operator|()", demangler.Parse("_Zorv"));
- ASSERT_EQ("operator|=()", demangler.Parse("_ZoRv"));
- ASSERT_EQ("operator->*()", demangler.Parse("_Zpmv"));
- ASSERT_EQ("operator+()", demangler.Parse("_Zplv"));
- ASSERT_EQ("operator+=()", demangler.Parse("_ZpLv"));
- ASSERT_EQ("operator++()", demangler.Parse("_Zppv"));
- ASSERT_EQ("operator+()", demangler.Parse("_Zpsv"));
- ASSERT_EQ("operator->()", demangler.Parse("_Zptv"));
- ASSERT_EQ("operator?()", demangler.Parse("_Zquv"));
- ASSERT_EQ("operator%()", demangler.Parse("_Zrmv"));
- ASSERT_EQ("operator%=()", demangler.Parse("_ZrMv"));
- ASSERT_EQ("operator>>()", demangler.Parse("_Zrsv"));
- ASSERT_EQ("operator>>=()", demangler.Parse("_ZrSv"));
-
- // Spot check using an operator as part of function name.
- ASSERT_EQ("operator&&()", demangler.Parse("_ZNaaEv"));
- ASSERT_EQ("operator++()", demangler.Parse("_ZNppEv"));
- ASSERT_EQ("one::operator++()", demangler.Parse("_ZN3oneppEv"));
-
- // Spot check using an operator in an argument name.
- ASSERT_EQ("operator+(operator|=)", demangler.Parse("_ZNpsENoRE"));
- ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
- ASSERT_EQ("one(arg1::operator|=, arg2::operator==)",
- demangler.Parse("_ZN3oneEN4arg1oREN4arg2eqE"));
-}
-
-TEST(DemangleTest, FunctionStartsWithNumber) {
- Demangler demangler;
-
- ASSERT_EQ("value(char, int)", demangler.Parse("_Z5valueci"));
- ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_Z11abcdefjklmna"));
- ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
-}
-
-TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
- Demangler demangler;
-
- ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
- ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
- ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
-}
-
-TEST(DemangleTest, StdTypes) {
- Demangler demangler;
-
- ASSERT_EQ("std::one", demangler.Parse("_ZNSt3oneE"));
- ASSERT_EQ("std::one(std::two)", demangler.Parse("_ZNSt3oneESt3two"));
- ASSERT_EQ("std::std::one(std::two)", demangler.Parse("_ZNStSt3oneESt3two"));
- ASSERT_EQ("std()", demangler.Parse("_ZNStEv"));
- ASSERT_EQ("one::std::std::two::~two(one::std::std::two)",
- demangler.Parse("_ZN3oneStSt3twoD0ES0_"));
-
- ASSERT_EQ("std::allocator", demangler.Parse("_ZNSaE"));
- ASSERT_EQ("std::basic_string", demangler.Parse("_ZNSbE"));
- ASSERT_EQ("_ZNScE", demangler.Parse("_ZNScE"));
- ASSERT_EQ("std::iostream", demangler.Parse("_ZNSdE"));
- ASSERT_EQ("_ZNSeE", demangler.Parse("_ZNSeE"));
- ASSERT_EQ("_ZNSfE", demangler.Parse("_ZNSfE"));
- ASSERT_EQ("_ZNSgE", demangler.Parse("_ZNSgE"));
- ASSERT_EQ("_ZNShE", demangler.Parse("_ZNShE"));
- ASSERT_EQ("std::istream", demangler.Parse("_ZNSiE"));
- ASSERT_EQ("_ZNSjE", demangler.Parse("_ZNSjE"));
- ASSERT_EQ("_ZNSkE", demangler.Parse("_ZNSkE"));
- ASSERT_EQ("_ZNSlE", demangler.Parse("_ZNSlE"));
- ASSERT_EQ("_ZNSmE", demangler.Parse("_ZNSmE"));
- ASSERT_EQ("_ZNSnE", demangler.Parse("_ZNSnE"));
- ASSERT_EQ("std::ostream", demangler.Parse("_ZNSoE"));
- ASSERT_EQ("_ZNSpE", demangler.Parse("_ZNSpE"));
- ASSERT_EQ("_ZNSqE", demangler.Parse("_ZNSqE"));
- ASSERT_EQ("_ZNSrE", demangler.Parse("_ZNSrE"));
- ASSERT_EQ("std::string", demangler.Parse("_ZNSsE"));
- ASSERT_EQ("_ZNSuE", demangler.Parse("_ZNSuE"));
- ASSERT_EQ("_ZNSvE", demangler.Parse("_ZNSvE"));
- ASSERT_EQ("_ZNSwE", demangler.Parse("_ZNSwE"));
- ASSERT_EQ("_ZNSxE", demangler.Parse("_ZNSxE"));
- ASSERT_EQ("_ZNSyE", demangler.Parse("_ZNSyE"));
- ASSERT_EQ("_ZNSzE", demangler.Parse("_ZNSzE"));
-}
-
-TEST(DemangleTest, SingleLetterArguments) {
- Demangler demangler;
-
- ASSERT_EQ("func(signed char)", demangler.Parse("_ZN4funcEa"));
- ASSERT_EQ("func(bool)", demangler.Parse("_ZN4funcEb"));
- ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
- ASSERT_EQ("func(double)", demangler.Parse("_ZN4funcEd"));
- ASSERT_EQ("func(long double)", demangler.Parse("_ZN4funcEe"));
- ASSERT_EQ("func(float)", demangler.Parse("_ZN4funcEf"));
- ASSERT_EQ("func(__float128)", demangler.Parse("_ZN4funcEg"));
- ASSERT_EQ("func(unsigned char)", demangler.Parse("_ZN4funcEh"));
- ASSERT_EQ("func(int)", demangler.Parse("_ZN4funcEi"));
- ASSERT_EQ("func(unsigned int)", demangler.Parse("_ZN4funcEj"));
- ASSERT_EQ("_ZN4funcEk", demangler.Parse("_ZN4funcEk"));
- ASSERT_EQ("func(long)", demangler.Parse("_ZN4funcEl"));
- ASSERT_EQ("func(unsigned long)", demangler.Parse("_ZN4funcEm"));
- ASSERT_EQ("func(__int128)", demangler.Parse("_ZN4funcEn"));
- ASSERT_EQ("func(unsigned __int128)", demangler.Parse("_ZN4funcEo"));
- ASSERT_EQ("_ZN4funcEp", demangler.Parse("_ZN4funcEp"));
- ASSERT_EQ("_ZN4funcEq", demangler.Parse("_ZN4funcEq"));
- ASSERT_EQ("_ZN4funcEr", demangler.Parse("_ZN4funcEr"));
- ASSERT_EQ("func(short)", demangler.Parse("_ZN4funcEs"));
- ASSERT_EQ("func(unsigned short)", demangler.Parse("_ZN4funcEt"));
- ASSERT_EQ("_ZN4funcEu", demangler.Parse("_ZN4funcEu"));
- ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
- ASSERT_EQ("func(wchar_t)", demangler.Parse("_ZN4funcEw"));
- ASSERT_EQ("func(long long)", demangler.Parse("_ZN4funcEx"));
- ASSERT_EQ("func(unsigned long long)", demangler.Parse("_ZN4funcEy"));
- ASSERT_EQ("func(...)", demangler.Parse("_ZN4funcEz"));
-}
-
-TEST(DemangleTest, DArguments) {
- Demangler demangler;
-
- ASSERT_EQ("func(auto)", demangler.Parse("_ZN4funcEDa"));
- ASSERT_EQ("_ZN4funcEDb", demangler.Parse("_ZN4funcEDb"));
- ASSERT_EQ("_ZN4funcEDc", demangler.Parse("_ZN4funcEDc"));
- ASSERT_EQ("func(decimal64)", demangler.Parse("_ZN4funcEDd"));
- ASSERT_EQ("func(decimal128)", demangler.Parse("_ZN4funcEDe"));
- ASSERT_EQ("func(decimal32)", demangler.Parse("_ZN4funcEDf"));
- ASSERT_EQ("_ZN4funcEDg", demangler.Parse("_ZN4funcEDg"));
- ASSERT_EQ("func(half)", demangler.Parse("_ZN4funcEDh"));
- ASSERT_EQ("func(char32_t)", demangler.Parse("_ZN4funcEDi"));
- ASSERT_EQ("_ZN4funcEDj", demangler.Parse("_ZN4funcEDj"));
- ASSERT_EQ("_ZN4funcEDk", demangler.Parse("_ZN4funcEDk"));
- ASSERT_EQ("_ZN4funcEDl", demangler.Parse("_ZN4funcEDl"));
- ASSERT_EQ("_ZN4funcEDm", demangler.Parse("_ZN4funcEDm"));
- ASSERT_EQ("func(decltype(nullptr))", demangler.Parse("_ZN4funcEDn"));
- ASSERT_EQ("_ZN4funcEDo", demangler.Parse("_ZN4funcEDo"));
- ASSERT_EQ("_ZN4funcEDp", demangler.Parse("_ZN4funcEDp"));
- ASSERT_EQ("_ZN4funcEDq", demangler.Parse("_ZN4funcEDq"));
- ASSERT_EQ("_ZN4funcEDr", demangler.Parse("_ZN4funcEDr"));
- ASSERT_EQ("func(char16_t)", demangler.Parse("_ZN4funcEDs"));
- ASSERT_EQ("_ZN4funcEDt", demangler.Parse("_ZN4funcEDt"));
- ASSERT_EQ("_ZN4funcEDu", demangler.Parse("_ZN4funcEDu"));
- ASSERT_EQ("_ZN4funcEDv", demangler.Parse("_ZN4funcEDv"));
- ASSERT_EQ("_ZN4funcEDw", demangler.Parse("_ZN4funcEDw"));
- ASSERT_EQ("_ZN4funcEDx", demangler.Parse("_ZN4funcEDx"));
- ASSERT_EQ("_ZN4funcEDy", demangler.Parse("_ZN4funcEDy"));
- ASSERT_EQ("_ZN4funcEDz", demangler.Parse("_ZN4funcEDz"));
-}
-
-TEST(DemangleTest, FunctionArguments) {
- Demangler demangler;
-
- ASSERT_EQ("func(char ())", demangler.Parse("_ZN4funcEFcvE"));
- ASSERT_EQ("func(char (*)())", demangler.Parse("_ZN4funcEPFcvE"));
- ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
- ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
- ASSERT_EQ("func(char (*&)())", demangler.Parse("_ZN4funcERPFcvE"));
- ASSERT_EQ("func(char (*)(int) const)", demangler.Parse("_ZN4funcEPKFciE"));
- ASSERT_EQ("func(char (&)() const)", demangler.Parse("_ZN4funcERKFcvE"));
- ASSERT_EQ("func(char (&)() volatile)", demangler.Parse("_ZN4funcERVFcvE"));
- ASSERT_EQ("func(char (&)() volatile const)", demangler.Parse("_ZN4funcERKVFcvE"));
- ASSERT_EQ("func(char (&)() const volatile)", demangler.Parse("_ZN4funcERVKFcvE"));
- ASSERT_EQ("func(char (&)(int, signed char) const)", demangler.Parse("_ZN4funcERKFciaE"));
- ASSERT_EQ("fake(char (&* volatile const)(void, void, signed char), signed char)",
- demangler.Parse("_ZN4fakeEKVPRFcvvaEa"));
-}
-
-TEST(DemangleTest, TemplateFunction) {
- Demangler demangler;
-
- ASSERT_EQ("one<char>", demangler.Parse("_ZN3oneIcEE"));
- ASSERT_EQ("one<void>", demangler.Parse("_ZN3oneIvEE"));
- ASSERT_EQ("one<void*>", demangler.Parse("_ZN3oneIPvEE"));
- ASSERT_EQ("one<void const>", demangler.Parse("_ZN3oneIKvEE"));
- ASSERT_EQ("one<char, int, bool>", demangler.Parse("_ZN3oneIcibEE"));
- ASSERT_EQ("one::two<three>", demangler.Parse("_ZN3one3twoIN5threeEEE"));
- ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_ZN3oneIciN3two5threeEEE"));
- // Template within templates.
- ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
- ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
-
- ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
- ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
- ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
- ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
- ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
- ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
- ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
- // Template within templates.
- ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
- ASSERT_EQ("one(two<three<char, four<int>>>)",
- demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
-}
-
-TEST(DemangleTest, TemplateFunctionWithReturnType) {
- Demangler demangler;
-
- ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
- ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
- ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
- ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
- ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
- ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
-}
-
-TEST(DemangleTest, TemplateArguments) {
- Demangler demangler;
-
- ASSERT_EQ("one(two<char>)", demangler.Parse("_ZN3oneE3twoIcE"));
- ASSERT_EQ("one(two<char, void>)", demangler.Parse("_ZN3oneE3twoIcvE"));
- ASSERT_EQ("one(two<char, void, three<four, int>>)",
- demangler.Parse("_ZN3oneE3twoIcv5threeI4fouriEE"));
-}
-
-TEST(DemangleTest, SubstitutionUnderscore) {
- Demangler demangler;
-
- ASSERT_EQ("a::a", demangler.Parse("_ZN1aS_E"));
- ASSERT_EQ("one::one", demangler.Parse("_ZN3oneS_E"));
- ASSERT_EQ("one::two::one", demangler.Parse("_ZN3one3twoS_E"));
- ASSERT_EQ("one::two::three::one", demangler.Parse("_ZN3one3two5threeS_E"));
- ASSERT_EQ("one::two(one)", demangler.Parse("_ZN3one3twoES_"));
- ASSERT_EQ("one::two(three::one)", demangler.Parse("_ZN3one3twoEN5threeS_E"));
-
- // Special case that St is part of the saved value used in the substitution.
- ASSERT_EQ("std::one::std::one", demangler.Parse("_ZNSt3oneS_E"));
-
- // Multiple substitutions in the string.
- ASSERT_EQ("one::one(one, one)", demangler.Parse("_ZN3oneS_ES_S_"));
- ASSERT_EQ("std::one::two::std::one(std::one)", demangler.Parse("_ZNSt3one3twoS_ES_"));
-}
-
-TEST(DemangleTest, SubstitutionByNumber) {
- Demangler demangler;
-
- // Basic substitution.
- ASSERT_EQ("a::b::c(a::b)", demangler.Parse("_ZN1a1b1cES0_"));
- ASSERT_EQ("_ZN1a1b1cES1_", demangler.Parse("_ZN1a1b1cES1_"));
- ASSERT_EQ("a::b::c::d(a::b::c)", demangler.Parse("_ZN1a1b1c1dES1_"));
- ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l)",
- demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESA_"));
- ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l::m)",
- demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESB_"));
-
- // Verify argument modifiers are included in substitution list.
- ASSERT_EQ("one::two(char&* volatile const, char&)", demangler.Parse("_ZN3one3twoEKVPRcS0_"));
- ASSERT_EQ("one::two(char&* volatile const, char&*)", demangler.Parse("_ZN3one3twoEKVPRcS1_"));
- ASSERT_EQ("one::two(char&* volatile const, char&* volatile const)",
- demangler.Parse("_ZN3one3twoEKVPRcS2_"));
- ASSERT_EQ("one::two(int&* volatile* const, int&)", demangler.Parse("_ZN3one3twoEKPVPRiS0_"));
- ASSERT_EQ("one::two(int&* volatile const, int&*)", demangler.Parse("_ZN3one3twoEKVPRiS1_"));
- ASSERT_EQ("one::two(int&* volatile const, int&* volatile const)",
- demangler.Parse("_ZN3one3twoEKVPRiS2_"));
-
- // Verify Constructor/Destructor does properly save from function name.
- ASSERT_EQ("_ZN1a1bES0_", demangler.Parse("_ZN1a1bES0_"));
- ASSERT_EQ("a::b::b(a::b)", demangler.Parse("_ZN1a1bC1ES0_"));
- ASSERT_EQ("a::b::~b(a::b)", demangler.Parse("_ZN1a1bD0ES0_"));
-
- // Make sure substitution values are not saved.
- ASSERT_EQ("a::b::b(a::b, char*, char*)", demangler.Parse("_ZN1a1bC1ES0_PcS1_"));
-}
-
-TEST(DemangleTest, ComplexSubstitution) {
- Demangler demangler;
-
- ASSERT_EQ("one::two<one::three>::two()", demangler.Parse("_ZN3one3twoINS_5threeEEC1Ev"));
- ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
- demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE"));
- ASSERT_EQ("one::two::three::four<one::five>::~four(one::two*)",
- demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS0_"));
- ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three*)",
- demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS1_"));
- ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three::four*)",
- demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS2_"));
- ASSERT_EQ("one::two::three::four<one::five>::~four(one::five*)",
- demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
-}
-
-TEST(DemangleTest, TemplateSubstitution) {
- Demangler demangler;
-
- ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
- ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
- ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
-
- ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
- ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
- ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
-
- ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
- demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
- ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
- demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
-
- ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
- demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
- ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
- demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
-}
-
-TEST(DemangleTest, StringTooLong) {
- Demangler demangler;
-
- ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
- demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 10));
- ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
- demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 30));
- ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
- demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 31));
-
- // Check the length check only occurs after the two letter value
- // has been processed.
- ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 15));
- ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 14));
- ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 13));
- ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
-}
-
-TEST(DemangleTest, BooleanLiterals) {
- Demangler demangler;
-
- ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
- ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
- ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
-
- ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
- ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
- ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
-
- ASSERT_EQ("one(two<three<four>, false, true>)",
- demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
-}
-
-TEST(DemangleTest, non_virtual_thunk) {
- Demangler demangler;
-
- ASSERT_EQ("non-virtual thunk to one", demangler.Parse("_ZThn0_N3oneE"));
- ASSERT_EQ("non-virtual thunk to two", demangler.Parse("_ZThn0_3two"));
- ASSERT_EQ("non-virtual thunk to three", demangler.Parse("_ZTh0_5three"));
- ASSERT_EQ("non-virtual thunk to four", demangler.Parse("_ZTh_4four"));
- ASSERT_EQ("non-virtual thunk to five", demangler.Parse("_ZTh0123456789_4five"));
- ASSERT_EQ("non-virtual thunk to six", demangler.Parse("_ZThn0123456789_3six"));
-
- ASSERT_EQ("_ZThn0N3oneE", demangler.Parse("_ZThn0N3oneE"));
- ASSERT_EQ("_ZThn03two", demangler.Parse("_ZThn03two"));
- ASSERT_EQ("_ZTh05three", demangler.Parse("_ZTh05three"));
- ASSERT_EQ("_ZTh4four", demangler.Parse("_ZTh4four"));
- ASSERT_EQ("_ZTh01234567894five", demangler.Parse("_ZTh01234567894five"));
- ASSERT_EQ("_ZThn01234567893six", demangler.Parse("_ZThn01234567893six"));
- ASSERT_EQ("_ZT_N3oneE", demangler.Parse("_ZT_N3oneE"));
- ASSERT_EQ("_ZT0_N3oneE", demangler.Parse("_ZT0_N3oneE"));
- ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
-}
-
-TEST(DemangleTest, r_value_reference) {
- Demangler demangler;
- ASSERT_EQ(
- "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
- "Transaction&&)",
- demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
-}
-
-TEST(DemangleTest, initial_St) {
- Demangler demangler;
- EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
- EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
- EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
-}
-
-TEST(DemangleTest, cfi) {
- Demangler demangler;
- EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
- demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
- EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
- demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
-}
-
-TEST(DemangleTest, demangle) {
- std::string str;
-
- str = demangle("_ZN1a1b1cES0_");
- ASSERT_EQ("a::b::c(a::b)", str);
-
- str = demangle("_");
- ASSERT_EQ("_", str);
-
- str = demangle("_Z");
- ASSERT_EQ("_Z", str);
-
- str = demangle("_Za");
- ASSERT_EQ("_Za", str);
-
- str = demangle("_Zaa");
- ASSERT_EQ("operator&&", str);
-
- str = demangle("Xa");
- ASSERT_EQ("Xa", str);
-}
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
deleted file mode 100644
index 7a3aa81..0000000
--- a/demangle/Demangler.cpp
+++ /dev/null
@@ -1,924 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-
-#include <cctype>
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "Demangler.h"
-
-constexpr const char* Demangler::kTypes[];
-constexpr const char* Demangler::kDTypes[];
-constexpr const char* Demangler::kSTypes[];
-
-void Demangler::Save(const std::string& str, bool is_name) {
- saves_.push_back(str);
- last_save_name_ = is_name;
-}
-
-std::string Demangler::GetArgumentsString() {
- size_t num_args = cur_state_.args.size();
- std::string arg_str;
- if (num_args > 0) {
- arg_str = cur_state_.args[0];
- for (size_t i = 1; i < num_args; i++) {
- arg_str += ", " + cur_state_.args[i];
- }
- }
- return arg_str;
-}
-
-const char* Demangler::AppendOperatorString(const char* name) {
- const char* oper = nullptr;
- switch (*name) {
- case 'a':
- name++;
- switch (*name) {
- case 'a':
- oper = "operator&&";
- break;
- case 'd':
- case 'n':
- oper = "operator&";
- break;
- case 'N':
- oper = "operator&=";
- break;
- case 'S':
- oper = "operator=";
- break;
- }
- break;
- case 'c':
- name++;
- switch (*name) {
- case 'l':
- oper = "operator()";
- break;
- case 'm':
- oper = "operator,";
- break;
- case 'o':
- oper = "operator~";
- break;
- }
- break;
- case 'd':
- name++;
- switch (*name) {
- case 'a':
- oper = "operator delete[]";
- break;
- case 'e':
- oper = "operator*";
- break;
- case 'l':
- oper = "operator delete";
- break;
- case 'v':
- oper = "operator/";
- break;
- case 'V':
- oper = "operator/=";
- break;
- }
- break;
- case 'e':
- name++;
- switch (*name) {
- case 'o':
- oper = "operator^";
- break;
- case 'O':
- oper = "operator^=";
- break;
- case 'q':
- oper = "operator==";
- break;
- }
- break;
- case 'g':
- name++;
- switch (*name) {
- case 'e':
- oper = "operator>=";
- break;
- case 't':
- oper = "operator>";
- break;
- }
- break;
- case 'i':
- name++;
- switch (*name) {
- case 'x':
- oper = "operator[]";
- break;
- }
- break;
- case 'l':
- name++;
- switch (*name) {
- case 'e':
- oper = "operator<=";
- break;
- case 's':
- oper = "operator<<";
- break;
- case 'S':
- oper = "operator<<=";
- break;
- case 't':
- oper = "operator<";
- break;
- }
- break;
- case 'm':
- name++;
- switch (*name) {
- case 'i':
- oper = "operator-";
- break;
- case 'I':
- oper = "operator-=";
- break;
- case 'l':
- oper = "operator*";
- break;
- case 'L':
- oper = "operator*=";
- break;
- case 'm':
- oper = "operator--";
- break;
- }
- break;
- case 'n':
- name++;
- switch (*name) {
- case 'a':
- oper = "operator new[]";
- break;
- case 'e':
- oper = "operator!=";
- break;
- case 'g':
- oper = "operator-";
- break;
- case 't':
- oper = "operator!";
- break;
- case 'w':
- oper = "operator new";
- break;
- }
- break;
- case 'o':
- name++;
- switch (*name) {
- case 'o':
- oper = "operator||";
- break;
- case 'r':
- oper = "operator|";
- break;
- case 'R':
- oper = "operator|=";
- break;
- }
- break;
- case 'p':
- name++;
- switch (*name) {
- case 'm':
- oper = "operator->*";
- break;
- case 'l':
- oper = "operator+";
- break;
- case 'L':
- oper = "operator+=";
- break;
- case 'p':
- oper = "operator++";
- break;
- case 's':
- oper = "operator+";
- break;
- case 't':
- oper = "operator->";
- break;
- }
- break;
- case 'q':
- name++;
- switch (*name) {
- case 'u':
- oper = "operator?";
- break;
- }
- break;
- case 'r':
- name++;
- switch (*name) {
- case 'm':
- oper = "operator%";
- break;
- case 'M':
- oper = "operator%=";
- break;
- case 's':
- oper = "operator>>";
- break;
- case 'S':
- oper = "operator>>=";
- break;
- }
- break;
- }
- if (oper == nullptr) {
- return nullptr;
- }
- AppendCurrent(oper);
- cur_state_.last_save = oper;
- return name + 1;
-}
-
-const char* Demangler::GetStringFromLength(const char* name, std::string* str) {
- assert(std::isdigit(*name));
-
- size_t length = *name - '0';
- name++;
- while (*name != '\0' && std::isdigit(*name)) {
- length = length * 10 + *name - '0';
- name++;
- }
-
- std::string read_str;
- while (*name != '\0' && length != 0) {
- read_str += *name;
- name++;
- length--;
- }
- if (length != 0) {
- return nullptr;
- }
- // Special replacement of _GLOBAL__N_1 to (anonymous namespace).
- if (read_str == "_GLOBAL__N_1") {
- *str += "(anonymous namespace)";
- } else {
- *str += read_str;
- }
- return name;
-}
-
-void Demangler::AppendCurrent(const std::string& str) {
- if (!cur_state_.str.empty()) {
- cur_state_.str += "::";
- }
- cur_state_.str += str;
-}
-
-void Demangler::AppendCurrent(const char* str) {
- if (!cur_state_.str.empty()) {
- cur_state_.str += "::";
- }
- cur_state_.str += str;
-}
-
-const char* Demangler::ParseS(const char* name) {
- if (std::islower(*name)) {
- const char* type = kSTypes[*name - 'a'];
- if (type == nullptr) {
- return nullptr;
- }
- AppendCurrent(type);
- return name + 1;
- }
-
- if (saves_.empty()) {
- return nullptr;
- }
-
- if (*name == '_') {
- last_save_name_ = false;
- AppendCurrent(saves_[0]);
- return name + 1;
- }
-
- bool isdigit = std::isdigit(*name);
- if (!isdigit && !std::isupper(*name)) {
- return nullptr;
- }
-
- size_t index;
- if (isdigit) {
- index = *name - '0' + 1;
- } else {
- index = *name - 'A' + 11;
- }
- name++;
- if (*name != '_') {
- return nullptr;
- }
-
- if (index >= saves_.size()) {
- return nullptr;
- }
-
- last_save_name_ = false;
- AppendCurrent(saves_[index]);
- return name + 1;
-}
-
-const char* Demangler::ParseT(const char* name) {
- if (template_saves_.empty()) {
- return nullptr;
- }
-
- if (*name == '_') {
- last_save_name_ = false;
- AppendCurrent(template_saves_[0]);
- return name + 1;
- }
-
- // Need to get the total number.
- char* end;
- unsigned long int index = strtoul(name, &end, 10) + 1;
- if (name == end || *end != '_') {
- return nullptr;
- }
-
- if (index >= template_saves_.size()) {
- return nullptr;
- }
-
- last_save_name_ = false;
- AppendCurrent(template_saves_[index]);
- return end + 1;
-}
-
-const char* Demangler::ParseFunctionName(const char* name) {
- if (*name == 'E') {
- if (parse_funcs_.empty()) {
- return nullptr;
- }
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
-
- // Remove the last saved part so that the full function name is not saved.
- // But only if the last save was not something like a substitution.
- if (!saves_.empty() && last_save_name_) {
- saves_.pop_back();
- }
-
- function_name_ += cur_state_.str;
- while (!cur_state_.suffixes.empty()) {
- function_suffix_ += cur_state_.suffixes.back();
- cur_state_.suffixes.pop_back();
- }
- cur_state_.Clear();
-
- return name + 1;
- }
-
- if (*name == 'I') {
- state_stack_.push(cur_state_);
- cur_state_.Clear();
-
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseFunctionNameTemplate;
- return name + 1;
- }
-
- return ParseComplexString(name);
-}
-
-const char* Demangler::ParseFunctionNameTemplate(const char* name) {
- if (*name == 'E' && name[1] == 'E') {
- // Only consider this a template with saves if it is right before
- // the end of the name.
- template_found_ = true;
- template_saves_ = cur_state_.args;
- }
- return ParseTemplateArgumentsComplex(name);
-}
-
-const char* Demangler::ParseComplexArgument(const char* name) {
- if (*name == 'E') {
- if (parse_funcs_.empty()) {
- return nullptr;
- }
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
-
- AppendArgument(cur_state_.str);
- cur_state_.str.clear();
-
- return name + 1;
- }
-
- return ParseComplexString(name);
-}
-
-void Demangler::FinalizeTemplate() {
- std::string arg_str(GetArgumentsString());
- cur_state_ = state_stack_.top();
- state_stack_.pop();
- cur_state_.str += '<' + arg_str + '>';
-}
-
-const char* Demangler::ParseComplexString(const char* name) {
- if (*name == 'S') {
- name++;
- if (*name == 't') {
- AppendCurrent("std");
- return name + 1;
- }
- return ParseS(name);
- }
- if (*name == 'L') {
- name++;
- if (!std::isdigit(*name)) {
- return nullptr;
- }
- }
- if (std::isdigit(*name)) {
- std::string str;
- name = GetStringFromLength(name, &str);
- if (name == nullptr) {
- return name;
- }
- AppendCurrent(str);
- Save(cur_state_.str, true);
- cur_state_.last_save = std::move(str);
- return name;
- }
- if (*name == 'D') {
- name++;
- if (saves_.empty() || (*name != '0' && *name != '1' && *name != '2'
- && *name != '5')) {
- return nullptr;
- }
- last_save_name_ = false;
- AppendCurrent("~" + cur_state_.last_save);
- return name + 1;
- }
- if (*name == 'C') {
- name++;
- if (saves_.empty() || (*name != '1' && *name != '2' && *name != '3'
- && *name != '5')) {
- return nullptr;
- }
- last_save_name_ = false;
- AppendCurrent(cur_state_.last_save);
- return name + 1;
- }
- if (*name == 'K') {
- cur_state_.suffixes.push_back(" const");
- return name + 1;
- }
- if (*name == 'V') {
- cur_state_.suffixes.push_back(" volatile");
- return name + 1;
- }
- if (*name == 'I') {
- // Save the current argument state.
- state_stack_.push(cur_state_);
- cur_state_.Clear();
-
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseTemplateArgumentsComplex;
- return name + 1;
- }
- name = AppendOperatorString(name);
- if (name != nullptr) {
- Save(cur_state_.str, true);
- }
- return name;
-}
-
-void Demangler::AppendArgument(const std::string& str) {
- std::string arg(str);
- while (!cur_state_.suffixes.empty()) {
- arg += cur_state_.suffixes.back();
- cur_state_.suffixes.pop_back();
- Save(arg, false);
- }
- cur_state_.args.push_back(arg);
-}
-
-const char* Demangler::ParseFunctionArgument(const char* name) {
- if (*name == 'E') {
- // The first argument is the function modifier.
- // The second argument is the function type.
- // The third argument is the return type of the function.
- // The rest of the arguments are the function arguments.
- size_t num_args = cur_state_.args.size();
- if (num_args < 4) {
- return nullptr;
- }
- std::string function_modifier = cur_state_.args[0];
- std::string function_type = cur_state_.args[1];
-
- std::string str = cur_state_.args[2] + ' ';
- if (!cur_state_.args[1].empty()) {
- str += '(' + cur_state_.args[1] + ')';
- }
-
- if (num_args == 4 && cur_state_.args[3] == "void") {
- str += "()";
- } else {
- str += '(' + cur_state_.args[3];
- for (size_t i = 4; i < num_args; i++) {
- str += ", " + cur_state_.args[i];
- }
- str += ')';
- }
- str += cur_state_.args[0];
-
- cur_state_ = state_stack_.top();
- state_stack_.pop();
- cur_state_.args.emplace_back(std::move(str));
-
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
- return name + 1;
- }
- return ParseArguments(name);
-}
-
-const char* Demangler::ParseArguments(const char* name) {
- switch (*name) {
- case 'P':
- cur_state_.suffixes.push_back("*");
- return name + 1;
-
- case 'R':
- // This should always be okay because the string is guaranteed to have
- // at least two characters before this. A mangled string always starts
- // with _Z.
- if (name[-1] != 'R') {
- // Multiple 'R's in a row only add a single &.
- cur_state_.suffixes.push_back("&");
- }
- return name + 1;
-
- case 'O':
- cur_state_.suffixes.push_back("&&");
- return name + 1;
-
- case 'K':
- case 'V': {
- const char* suffix;
- if (*name == 'K') {
- suffix = " const";
- } else {
- suffix = " volatile";
- }
- if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
- // Special case, const/volatile apply as a single entity.
- size_t index = cur_state_.suffixes.size();
- cur_state_.suffixes[index-1].insert(0, suffix);
- } else {
- cur_state_.suffixes.push_back(suffix);
- }
- return name + 1;
- }
-
- case 'F': {
- std::string function_modifier;
- std::string function_type;
- if (!cur_state_.suffixes.empty()) {
- // If the first element starts with a ' ', then this modifies the
- // function itself.
- if (cur_state_.suffixes.back()[0] == ' ') {
- function_modifier = cur_state_.suffixes.back();
- cur_state_.suffixes.pop_back();
- }
- while (!cur_state_.suffixes.empty()) {
- function_type += cur_state_.suffixes.back();
- cur_state_.suffixes.pop_back();
- }
- }
-
- state_stack_.push(cur_state_);
-
- cur_state_.Clear();
-
- // The function parameter has this format:
- // First argument is the function modifier.
- // Second argument is the function type.
- // Third argument will be the return function type but has not
- // been parsed yet.
- // Any other parameters are the arguments to the function. There
- // must be at least one or this isn't valid.
- cur_state_.args.push_back(function_modifier);
- cur_state_.args.push_back(function_type);
-
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseFunctionArgument;
- return name + 1;
- }
-
- case 'N':
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseComplexArgument;
- return name + 1;
-
- case 'S':
- name++;
- if (*name == 't') {
- cur_state_.str = "std::";
- return name + 1;
- }
- name = ParseS(name);
- if (name == nullptr) {
- return nullptr;
- }
- AppendArgument(cur_state_.str);
- cur_state_.str.clear();
- return name;
-
- case 'D':
- name++;
- if (*name >= 'a' && *name <= 'z') {
- const char* arg = Demangler::kDTypes[*name - 'a'];
- if (arg == nullptr) {
- return nullptr;
- }
- AppendArgument(arg);
- return name + 1;
- }
- return nullptr;
-
- case 'I':
- // Save the current argument state.
- state_stack_.push(cur_state_);
- cur_state_.Clear();
-
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseTemplateArguments;
- return name + 1;
-
- case 'v':
- AppendArgument("void");
- return name + 1;
-
- default:
- if (*name >= 'a' && *name <= 'z') {
- const char* arg = Demangler::kTypes[*name - 'a'];
- if (arg == nullptr) {
- return nullptr;
- }
- AppendArgument(arg);
- return name + 1;
- } else if (std::isdigit(*name)) {
- std::string arg = cur_state_.str;
- name = GetStringFromLength(name, &arg);
- if (name == nullptr) {
- return nullptr;
- }
- Save(arg, true);
- if (*name == 'I') {
- // There is one case where this argument is not complete, and that's
- // where this is a template argument.
- cur_state_.str = arg;
- } else {
- AppendArgument(arg);
- cur_state_.str.clear();
- }
- return name;
- } else if (strcmp(name, ".cfi") == 0) {
- function_suffix_ += " [clone .cfi]";
- return name + 4;
- }
- }
- return nullptr;
-}
-
-const char* Demangler::ParseTemplateLiteral(const char* name) {
- if (*name == 'E') {
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
- return name + 1;
- }
- // Only understand boolean values with 0 or 1.
- if (*name == 'b') {
- name++;
- if (*name == '0') {
- AppendArgument("false");
- cur_state_.str.clear();
- } else if (*name == '1') {
- AppendArgument("true");
- cur_state_.str.clear();
- } else {
- return nullptr;
- }
- return name + 1;
- }
- return nullptr;
-}
-
-const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
- if (*name == 'E') {
- if (parse_funcs_.empty()) {
- return nullptr;
- }
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
-
- FinalizeTemplate();
- Save(cur_state_.str, false);
- return name + 1;
- } else if (*name == 'L') {
- // Literal value for a template.
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseTemplateLiteral;
- return name + 1;
- }
-
- return ParseArguments(name);
-}
-
-const char* Demangler::ParseTemplateArguments(const char* name) {
- if (*name == 'E') {
- if (parse_funcs_.empty()) {
- return nullptr;
- }
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
- FinalizeTemplate();
- AppendArgument(cur_state_.str);
- cur_state_.str.clear();
- return name + 1;
- } else if (*name == 'L') {
- // Literal value for a template.
- parse_funcs_.push_back(parse_func_);
- parse_func_ = &Demangler::ParseTemplateLiteral;
- return name + 1;
- }
-
- return ParseArguments(name);
-}
-
-const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
- if (*name == 'E') {
- parse_func_ = parse_funcs_.back();
- parse_funcs_.pop_back();
-
- function_name_ += '<' + GetArgumentsString() + '>';
- template_found_ = true;
- template_saves_ = cur_state_.args;
- cur_state_.Clear();
- return name + 1;
- }
- return ParseTemplateArgumentsComplex(name);
-}
-
-const char* Demangler::FindFunctionName(const char* name) {
- if (*name == 'T') {
- // non-virtual thunk, verify that it matches one of these patterns:
- // Thn[0-9]+_
- // Th[0-9]+_
- // Thn_
- // Th_
- name++;
- if (*name != 'h') {
- return nullptr;
- }
- name++;
- if (*name == 'n') {
- name++;
- }
- while (std::isdigit(*name)) {
- name++;
- }
- if (*name != '_') {
- return nullptr;
- }
- function_name_ = "non-virtual thunk to ";
- return name + 1;
- }
-
- if (*name == 'N') {
- parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
- parse_func_ = &Demangler::ParseFunctionName;
- return name + 1;
- }
-
- if (*name == 'S') {
- name++;
- if (*name == 't') {
- function_name_ = "std::";
- name++;
- } else {
- return nullptr;
- }
- }
-
- if (std::isdigit(*name)) {
- name = GetStringFromLength(name, &function_name_);
- } else if (*name == 'L' && std::isdigit(name[1])) {
- name = GetStringFromLength(name + 1, &function_name_);
- } else {
- name = AppendOperatorString(name);
- function_name_ = cur_state_.str;
- }
- cur_state_.Clear();
-
- // Check for a template argument, which will still be part of the function
- // name.
- if (name != nullptr && *name == 'I') {
- parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
- parse_func_ = &Demangler::ParseFunctionTemplateArguments;
- return name + 1;
- }
- parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
- return name;
-}
-
-const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
- // At the top level is the only place where T is allowed.
- if (*name == 'T') {
- name++;
- name = ParseT(name);
- if (name == nullptr) {
- return nullptr;
- }
- AppendArgument(cur_state_.str);
- cur_state_.str.clear();
- return name;
- }
-
- return Demangler::ParseArguments(name);
-}
-
-std::string Demangler::Parse(const char* name, size_t max_length) {
- if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
- // Name is not mangled.
- return name;
- }
-
- Clear();
-
- parse_func_ = &Demangler::FindFunctionName;
- parse_funcs_.push_back(&Demangler::Fail);
- const char* cur_name = name + 2;
- while (cur_name != nullptr && *cur_name != '\0'
- && static_cast<size_t>(cur_name - name) < max_length) {
- cur_name = (this->*parse_func_)(cur_name);
- }
- if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
- !cur_state_.suffixes.empty()) {
- return name;
- }
-
- std::string return_type;
- if (template_found_) {
- // Only a single argument with a template is not allowed.
- if (cur_state_.args.size() == 1) {
- return name;
- }
-
- // If there are at least two arguments, this template has a return type.
- if (cur_state_.args.size() > 1) {
- // The first argument will be the return value.
- return_type = cur_state_.args[0] + ' ';
- cur_state_.args.erase(cur_state_.args.begin());
- }
- }
-
- std::string arg_str;
- if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
- // If the only argument is void, then don't print any args.
- arg_str = "()";
- } else {
- arg_str = GetArgumentsString();
- if (!arg_str.empty()) {
- arg_str = '(' + arg_str + ')';
- }
- }
- return return_type + function_name_ + arg_str + function_suffix_;
-}
-
-std::string demangle(const char* name) {
- Demangler demangler;
- return demangler.Parse(name);
-}
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
deleted file mode 100644
index 3b7d44e..0000000
--- a/demangle/Demangler.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIB_DEMANGLE_DEMANGLER_H
-#define __LIB_DEMANGLE_DEMANGLER_H
-
-#include <assert.h>
-
-#include <stack>
-#include <string>
-#include <vector>
-
-class Demangler {
- public:
- Demangler() = default;
-
- // NOTE: The max_length is not guaranteed to be the absolute max length
- // of a string that will be rejected. Under certain circumstances the
- // length check will not occur until after the second letter of a pair
- // is checked.
- std::string Parse(const char* name, size_t max_length = kMaxDefaultLength);
-
- void AppendCurrent(const std::string& str);
- void AppendCurrent(const char* str);
- void AppendArgument(const std::string& str);
- std::string GetArgumentsString();
- void FinalizeTemplate();
- const char* ParseS(const char* name);
- const char* ParseT(const char* name);
- const char* AppendOperatorString(const char* name);
- void Save(const std::string& str, bool is_name);
-
- private:
- void Clear() {
- parse_funcs_.clear();
- function_name_.clear();
- function_suffix_.clear();
- first_save_.clear();
- cur_state_.Clear();
- saves_.clear();
- template_saves_.clear();
- while (!state_stack_.empty()) {
- state_stack_.pop();
- }
- last_save_name_ = false;
- template_found_ = false;
- }
-
- using parse_func_type = const char* (Demangler::*)(const char*);
- parse_func_type parse_func_;
- std::vector<parse_func_type> parse_funcs_;
- std::vector<std::string> saves_;
- std::vector<std::string> template_saves_;
- bool last_save_name_;
- bool template_found_;
-
- std::string function_name_;
- std::string function_suffix_;
-
- struct StateData {
- void Clear() {
- str.clear();
- args.clear();
- prefix.clear();
- suffixes.clear();
- last_save.clear();
- }
-
- std::string str;
- std::vector<std::string> args;
- std::string prefix;
- std::vector<std::string> suffixes;
- std::string last_save;
- };
- std::stack<StateData> state_stack_;
- std::string first_save_;
- StateData cur_state_;
-
- static const char* GetStringFromLength(const char* name, std::string* str);
-
- // Parsing functions.
- const char* ParseComplexString(const char* name);
- const char* ParseComplexArgument(const char* name);
- const char* ParseArgumentsAtTopLevel(const char* name);
- const char* ParseArguments(const char* name);
- const char* ParseTemplateArguments(const char* name);
- const char* ParseTemplateArgumentsComplex(const char* name);
- const char* ParseTemplateLiteral(const char* name);
- const char* ParseFunctionArgument(const char* name);
- const char* ParseFunctionName(const char* name);
- const char* ParseFunctionNameTemplate(const char* name);
- const char* ParseFunctionTemplateArguments(const char* name);
- const char* FindFunctionName(const char* name);
- const char* Fail(const char*) { return nullptr; }
-
- // The default maximum string length string to process.
- static constexpr size_t kMaxDefaultLength = 2048;
-
- static constexpr const char* kTypes[] = {
- "signed char", // a
- "bool", // b
- "char", // c
- "double", // d
- "long double", // e
- "float", // f
- "__float128", // g
- "unsigned char", // h
- "int", // i
- "unsigned int", // j
- nullptr, // k
- "long", // l
- "unsigned long", // m
- "__int128", // n
- "unsigned __int128", // o
- nullptr, // p
- nullptr, // q
- nullptr, // r
- "short", // s
- "unsigned short", // t
- nullptr, // u
- "void", // v
- "wchar_t", // w
- "long long", // x
- "unsigned long long", // y
- "...", // z
- };
-
- static constexpr const char* kDTypes[] = {
- "auto", // a
- nullptr, // b
- nullptr, // c
- "decimal64", // d
- "decimal128", // e
- "decimal32", // f
- nullptr, // g
- "half", // h
- "char32_t", // i
- nullptr, // j
- nullptr, // k
- nullptr, // l
- nullptr, // m
- "decltype(nullptr)", // n
- nullptr, // o
- nullptr, // p
- nullptr, // q
- nullptr, // r
- "char16_t", // s
- nullptr, // t
- nullptr, // u
- nullptr, // v
- nullptr, // w
- nullptr, // x
- nullptr, // y
- nullptr, // z
- };
-
- static constexpr const char* kSTypes[] = {
- "std::allocator", // a
- "std::basic_string", // b
- nullptr, // c
- "std::iostream", // d
- nullptr, // e
- nullptr, // f
- nullptr, // g
- nullptr, // h
- "std::istream", // i
- nullptr, // j
- nullptr, // k
- nullptr, // l
- nullptr, // m
- nullptr, // n
- "std::ostream", // o
- nullptr, // p
- nullptr, // q
- nullptr, // r
- "std::string", // s
- nullptr, // t
- nullptr, // u
- nullptr, // v
- nullptr, // w
- nullptr, // x
- nullptr, // y
- nullptr, // z
- };
-};
-
-#endif // __LIB_DEMANGLE_DEMANGLER_H
diff --git a/demangle/OWNERS b/demangle/OWNERS
deleted file mode 100644
index 6f7e4a3..0000000
--- a/demangle/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-cferris@google.com
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
deleted file mode 100644
index 66e5e58..0000000
--- a/demangle/demangle.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cctype>
-#include <string>
-
-#include <demangle.h>
-
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-
-static void Usage(const char* prog_name) {
- printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
- printf("\n");
- printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
- printf("reading from stdin otherwise.\n");
- printf("\n");
- printf("-c\tCompare against __cxa_demangle\n");
- printf("\n");
-}
-
-static std::string DemangleWithCxa(const char* name) {
- const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
- if (cxa_demangle == nullptr) {
- return name;
- }
-
- // The format of our demangler is slightly different from the cxa demangler
- // so modify the cxa demangler output. Specifically, for templates, remove
- // the spaces between '>' and '>'.
- std::string demangled_str;
- for (size_t i = 0; i < strlen(cxa_demangle); i++) {
- if (i > 2 && cxa_demangle[i] == '>' && std::isspace(cxa_demangle[i - 1]) &&
- cxa_demangle[i - 2] == '>') {
- demangled_str.resize(demangled_str.size() - 1);
- }
- demangled_str += cxa_demangle[i];
- }
- return demangled_str;
-}
-
-static void Compare(const char* name, const std::string& demangled_name) {
- std::string cxa_demangled_name(DemangleWithCxa(name));
- if (cxa_demangled_name != demangled_name) {
- printf("\nMismatch!\n");
- printf("\tmangled name: %s\n", name);
- printf("\tour demangle: %s\n", demangled_name.c_str());
- printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
- exit(1);
- }
-}
-
-static int Filter(bool compare) {
- char* line = nullptr;
- size_t line_length = 0;
-
- while ((getline(&line, &line_length, stdin)) != -1) {
- char* p = line;
- char* name;
- while ((name = strstr(p, "_Z")) != nullptr) {
- // Output anything before the identifier.
- *name = 0;
- printf("%s", p);
- *name = '_';
-
- // Extract the identifier.
- p = name;
- while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
-
- // Demangle and output.
- std::string identifier(name, p);
- std::string demangled_name = demangle(identifier.c_str());
- printf("%s", demangled_name.c_str());
-
- if (compare) Compare(identifier.c_str(), demangled_name);
- }
- // Output anything after the last identifier.
- printf("%s", p);
- }
-
- free(line);
- return 0;
-}
-
-int main(int argc, char** argv) {
-#ifdef __BIONIC__
- const char* prog_name = getprogname();
-#else
- const char* prog_name = argv[0];
-#endif
-
- bool compare = false;
- int opt_char;
- while ((opt_char = getopt(argc, argv, "c")) != -1) {
- if (opt_char == 'c') {
- compare = true;
- } else {
- Usage(prog_name);
- return 1;
- }
- }
-
- // With no arguments, act as a filter.
- if (optind == argc) return Filter(compare);
-
- // Otherwise demangle each argument.
- while (optind < argc) {
- const char* name = argv[optind++];
- std::string demangled_name = demangle(name);
- printf("%s\n", demangled_name.c_str());
-
- if (compare) Compare(name, demangled_name);
- }
- return 0;
-}
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
deleted file mode 100644
index 83fafc2..0000000
--- a/demangle/demangle_fuzzer.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "Demangler.h"
-
-extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- std::vector<char> data_str(size + 1);
- memcpy(data_str.data(), data, size);
- data_str[size] = '\0';
-
- Demangler demangler;
- std::string demangled_name = demangler.Parse(data_str.data());
- if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
- abort();
- }
-}
diff --git a/demangle/include/demangle.h b/demangle/include/demangle.h
deleted file mode 100644
index 01f1b80..0000000
--- a/demangle/include/demangle.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIB_DEMANGLE_H_
-#define __LIB_DEMANGLE_H_
-
-#include <string>
-
-// If the name cannot be demangled, the original name will be returned as
-// a std::string. If the name can be demangled, then the demangled name
-// will be returned as a std::string.
-std::string demangle(const char* name);
-
-#endif // __LIB_DEMANGLE_H_
diff --git a/adf/Android.bp b/deprecated-adf/Android.bp
similarity index 100%
rename from adf/Android.bp
rename to deprecated-adf/Android.bp
diff --git a/adf/OWNERS b/deprecated-adf/OWNERS
similarity index 100%
rename from adf/OWNERS
rename to deprecated-adf/OWNERS
diff --git a/adf/libadf/Android.bp b/deprecated-adf/libadf/Android.bp
similarity index 100%
rename from adf/libadf/Android.bp
rename to deprecated-adf/libadf/Android.bp
diff --git a/adf/libadf/adf.cpp b/deprecated-adf/libadf/adf.cpp
similarity index 100%
rename from adf/libadf/adf.cpp
rename to deprecated-adf/libadf/adf.cpp
diff --git a/adf/libadf/include/adf/adf.h b/deprecated-adf/libadf/include/adf/adf.h
similarity index 100%
rename from adf/libadf/include/adf/adf.h
rename to deprecated-adf/libadf/include/adf/adf.h
diff --git a/adf/libadf/include/video/adf.h b/deprecated-adf/libadf/include/video/adf.h
similarity index 100%
rename from adf/libadf/include/video/adf.h
rename to deprecated-adf/libadf/include/video/adf.h
diff --git a/adf/libadf/original-kernel-headers/video/adf.h b/deprecated-adf/libadf/original-kernel-headers/video/adf.h
similarity index 100%
rename from adf/libadf/original-kernel-headers/video/adf.h
rename to deprecated-adf/libadf/original-kernel-headers/video/adf.h
diff --git a/adf/libadf/tests/Android.bp b/deprecated-adf/libadf/tests/Android.bp
similarity index 100%
rename from adf/libadf/tests/Android.bp
rename to deprecated-adf/libadf/tests/Android.bp
diff --git a/adf/libadf/tests/adf_test.cpp b/deprecated-adf/libadf/tests/adf_test.cpp
similarity index 100%
rename from adf/libadf/tests/adf_test.cpp
rename to deprecated-adf/libadf/tests/adf_test.cpp
diff --git a/adf/libadfhwc/Android.bp b/deprecated-adf/libadfhwc/Android.bp
similarity index 100%
rename from adf/libadfhwc/Android.bp
rename to deprecated-adf/libadfhwc/Android.bp
diff --git a/adf/libadfhwc/adfhwc.cpp b/deprecated-adf/libadfhwc/adfhwc.cpp
similarity index 100%
rename from adf/libadfhwc/adfhwc.cpp
rename to deprecated-adf/libadfhwc/adfhwc.cpp
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
similarity index 100%
rename from adf/libadfhwc/include/adfhwc/adfhwc.h
rename to deprecated-adf/libadfhwc/include/adfhwc/adfhwc.h
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 50d18ed..716fe95 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -43,6 +43,7 @@
"libgtest_main",
"libbase",
"libadb_host",
+ "liblp",
],
header_libs: [
@@ -131,6 +132,7 @@
"libext2_uuid",
"libext4_utils",
"libfs_mgr",
+ "libgsi",
"libhidlbase",
"libhidltransport",
"libhwbinder",
@@ -143,8 +145,6 @@
static_libs: [
"libhealthhalutils",
],
-
- cpp_std: "c++17",
}
cc_defaults {
@@ -173,6 +173,11 @@
host_ldlibs: ["-lws2_32"],
},
+ not_windows: {
+ static_libs: [
+ "libext4_utils",
+ ],
+ },
},
stl: "libc++_static",
@@ -193,6 +198,8 @@
"libbase",
"libcutils",
"libgtest_host",
+ "liblp",
+ "libcrypto",
],
}
@@ -204,7 +211,6 @@
name: "libfastboot",
defaults: ["fastboot_host_defaults"],
- cpp_std: "c++17",
srcs: [
"bootimg_utils.cpp",
"fastboot.cpp",
@@ -252,6 +258,13 @@
"mke2fs",
"make_f2fs",
],
+ dist: {
+ targets: [
+ "dist_files",
+ "sdk",
+ "win_sdk",
+ ],
+ },
target: {
not_windows: {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index e4c1317..17ec392 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,13 +18,9 @@
# Package fastboot-related executables.
#
-my_dist_files := $(HOST_OUT_EXECUTABLES)/fastboot
-my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
-ifdef HOST_CROSS_OS
-$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
-endif
my_dist_files :=
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index e433787..46d4bd3 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -34,25 +34,27 @@
#include <stdlib.h>
#include <string.h>
-void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline) {
+void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {
if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
-boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
- const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
- std::vector<char>* out) {
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, const std::vector<char>& dtb,
+ size_t base, const boot_img_hdr_v2& src, std::vector<char>* out) {
const size_t page_mask = src.page_size - 1;
int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
int64_t second_actual = (second.size() + page_mask) & (~page_mask);
+ int64_t dtb_actual = (dtb.size() + page_mask) & (~page_mask);
- int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+ int64_t bootimg_size =
+ header_actual + kernel_actual + ramdisk_actual + second_actual + dtb_actual;
out->resize(bootimg_size);
- boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
+ boot_img_hdr_v2* hdr = reinterpret_cast<boot_img_hdr_v2*>(out->data());
*hdr = src;
memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
@@ -66,13 +68,19 @@
hdr->second_addr += base;
hdr->tags_addr += base;
- if (hdr->header_version != 0) {
+ if (hdr->header_version == 1) {
hdr->header_size = sizeof(boot_img_hdr_v1);
+ } else if (hdr->header_version == 2) {
+ hdr->header_size = sizeof(boot_img_hdr_v2);
+ hdr->dtb_size = dtb.size();
+ hdr->dtb_addr += base;
}
memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
second.size());
+ memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual + second_actual, dtb.data(),
+ dtb.size());
return hdr;
}
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index a4e8870..b7cf9bd 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -35,7 +35,7 @@
#include <string>
#include <vector>
-boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
- const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
- std::vector<char>* out);
-void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
+boot_img_hdr_v2* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+ const std::vector<char>& second, const std::vector<char>& dtb,
+ size_t base, const boot_img_hdr_v2& src, std::vector<char>* out);
+void bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 705da33..8a72627 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -33,6 +33,7 @@
#define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
#define FB_CMD_UPDATE_SUPER "update-super"
#define FB_CMD_OEM "oem"
+#define FB_CMD_GSI "gsi"
#define RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@@ -64,3 +65,4 @@
#define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
#define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
#define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
+#define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 6e45133..409ef70 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -28,6 +28,8 @@
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
+#include <fs_mgr/roots.h>
+#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <uuid/uuid.h>
@@ -37,6 +39,7 @@
#include "flashing.h"
#include "utility.h"
+using android::fs_mgr::MetadataBuilder;
using ::android::hardware::hidl_string;
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
@@ -44,8 +47,6 @@
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
-using namespace android::fs_mgr;
-
struct VariableHandlers {
// Callback to retrieve the value of a single variable.
std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
@@ -99,7 +100,8 @@
{FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
{FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
{FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
- {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
+ {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+ {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
if (args.size() < 2) {
return device->WriteFail("Missing argument");
@@ -321,36 +323,37 @@
// partition table to the same place it was read.
class PartitionBuilder {
public:
- explicit PartitionBuilder(FastbootDevice* device);
+ explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);
bool Write();
bool Valid() const { return !!builder_; }
MetadataBuilder* operator->() const { return builder_.get(); }
private:
+ FastbootDevice* device_;
std::string super_device_;
uint32_t slot_number_;
std::unique_ptr<MetadataBuilder> builder_;
};
-PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
- auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
+ : device_(device) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+ slot_number_ = android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
+ auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
if (!super_device) {
return;
}
super_device_ = *super_device;
-
- std::string slot = device->GetCurrentSlot();
- slot_number_ = SlotNumberForSlotSuffix(slot);
builder_ = MetadataBuilder::New(super_device_, slot_number_);
}
bool PartitionBuilder::Write() {
- std::unique_ptr<LpMetadata> metadata = builder_->Export();
+ auto metadata = builder_->Export();
if (!metadata) {
return false;
}
- return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+ return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -368,7 +371,7 @@
return device->WriteFail("Invalid partition size");
}
- PartitionBuilder builder(device);
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
@@ -377,7 +380,7 @@
return device->WriteFail("Partition already exists");
}
- Partition* partition = builder->AddPartition(partition_name, 0);
+ auto partition = builder->AddPartition(partition_name, 0);
if (!partition) {
return device->WriteFail("Failed to add partition");
}
@@ -400,11 +403,13 @@
return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
}
- PartitionBuilder builder(device);
+ std::string partition_name = args[1];
+
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
- builder->RemovePartition(args[1]);
+ builder->RemovePartition(partition_name);
if (!builder.Write()) {
return device->WriteFail("Failed to write partition table");
}
@@ -426,12 +431,12 @@
return device->WriteFail("Invalid partition size");
}
- PartitionBuilder builder(device);
+ PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
- Partition* partition = builder->FindPartition(partition_name);
+ auto partition = builder->FindPartition(partition_name);
if (!partition) {
return device->WriteFail("Partition does not exist");
}
@@ -456,3 +461,65 @@
bool wipe = (args.size() >= 3 && args[2] == "wipe");
return UpdateSuper(device, args[1], wipe);
}
+
+class AutoMountMetadata {
+ public:
+ AutoMountMetadata() {
+ android::fs_mgr::Fstab proc_mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+ LOG(ERROR) << "Could not read /proc/mounts";
+ return;
+ }
+
+ auto iter = std::find_if(proc_mounts.begin(), proc_mounts.end(),
+ [](const auto& entry) { return entry.mount_point == "/metadata"; });
+ if (iter != proc_mounts.end()) {
+ mounted_ = true;
+ return;
+ }
+
+ if (!ReadDefaultFstab(&fstab_)) {
+ LOG(ERROR) << "Could not read default fstab";
+ return;
+ }
+ mounted_ = EnsurePathMounted(&fstab_, "/metadata");
+ should_unmount_ = true;
+ }
+ ~AutoMountMetadata() {
+ if (mounted_ && should_unmount_) {
+ EnsurePathUnmounted(&fstab_, "/metadata");
+ }
+ }
+ explicit operator bool() const { return mounted_; }
+
+ private:
+ android::fs_mgr::Fstab fstab_;
+ bool mounted_ = false;
+ bool should_unmount_ = false;
+};
+
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() != 2) {
+ return device->WriteFail("Invalid arguments");
+ }
+
+ AutoMountMetadata mount_metadata;
+ if (!mount_metadata) {
+ return device->WriteFail("Could not find GSI install");
+ }
+
+ if (!android::gsi::IsGsiInstalled()) {
+ return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
+ }
+
+ if (args[1] == "wipe") {
+ if (!android::gsi::UninstallGsi()) {
+ return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+ }
+ } else if (args[1] == "disable") {
+ if (!android::gsi::DisableGsi()) {
+ return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
+ }
+ }
+ return device->WriteStatus(FastbootResult::OKAY, "Success");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index bb1f988..afd6d08 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -48,3 +48,4 @@
bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 7be721a..56fafab 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -53,6 +53,7 @@
{FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
{FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
{FB_CMD_OEM, OemCmdHandler},
+ {FB_CMD_GSI, GsiHandler},
}),
transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 66b90bf..99854c9 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -52,15 +52,17 @@
// Following appears to have a first time 2% impact on flashing speeds.
// Convert partition_name to a validated mount point and wipe.
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto mount_point = fstab->recs[i].mount_point;
- if (!mount_point) continue;
- auto partition = android::base::Basename(mount_point);
- if ("/"s == mount_point) partition = "system";
+ Fstab fstab;
+ ReadDefaultFstab(&fstab);
+
+ for (const auto& entry : fstab) {
+ auto partition = android::base::Basename(entry.mount_point);
+ if ("/" == entry.mount_point) {
+ partition = "system";
+ }
+
if ((partition + device->GetCurrentSlot()) == partition_name) {
- fs_mgr_overlayfs_teardown(mount_point);
+ fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
}
}
}
@@ -143,26 +145,55 @@
return device->WriteFail("Data is not a valid logical partition metadata image");
}
+ if (!FindPhysicalPartition(super_name)) {
+ return device->WriteFail("Cannot find " + super_name +
+ ", build may be missing broken or missing boot_devices");
+ }
+
// If we are unable to read the existing metadata, then the super partition
// is corrupt. In this case we reflash the whole thing using the provided
// image.
std::string slot_suffix = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
- if (wipe || !ReadMetadata(super_name, slot_number)) {
+ std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
+ if (wipe || !old_metadata) {
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
+ fs_mgr_overlayfs_teardown();
return device->WriteOkay("Successfully flashed partition table");
}
- // Write the new table to every metadata slot.
- bool ok = true;
- for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
- ok &= UpdatePartitionTable(super_name, *new_metadata.get(), i);
+ std::set<std::string> partitions_to_keep;
+ for (const auto& partition : old_metadata->partitions) {
+ // Preserve partitions in the other slot, but not the current slot.
+ std::string partition_name = GetPartitionName(partition);
+ if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+ continue;
+ }
+ partitions_to_keep.emplace(partition_name);
}
- if (!ok) {
+ // Do not preserve the scratch partition.
+ partitions_to_keep.erase("scratch");
+
+ if (!partitions_to_keep.empty()) {
+ std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());
+ if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {
+ return device->WriteFail(
+ "Old partitions are not compatible with the new super layout; wipe needed");
+ }
+
+ new_metadata = builder->Export();
+ if (!new_metadata) {
+ return device->WriteFail("Unable to build new partition table; wipe needed");
+ }
+ }
+
+ // Write the new table to every metadata slot.
+ if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
+ fs_mgr_overlayfs_teardown();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index fb51a90..5066046 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -33,7 +33,7 @@
constexpr int kMaxPacketsizeSs = 1024;
constexpr size_t kFbFfsNumBufs = 16;
-constexpr size_t kFbFfsBufSize = 32768;
+constexpr size_t kFbFfsBufSize = 16384;
constexpr const char* kUsbFfsFastbootEp0 = "/dev/usb-ffs/fastboot/ep0";
constexpr const char* kUsbFfsFastbootOut = "/dev/usb-ffs/fastboot/ep1";
@@ -255,9 +255,10 @@
size_t bytes_read_total = 0;
while (bytes_read_total < len) {
auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);
- auto bytes_read_now = handle_->read(handle_.get(), char_data, bytes_to_read);
+ auto bytes_read_now =
+ handle_->read(handle_.get(), char_data, bytes_to_read, true /* allow_partial */);
if (bytes_read_now < 0) {
- return bytes_read_total;
+ return bytes_read_total == 0 ? -1 : bytes_read_total;
}
bytes_read_total += bytes_read_now;
char_data += bytes_read_now;
@@ -278,7 +279,7 @@
auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
if (bytes_written_now < 0) {
- return bytes_written_total;
+ return bytes_written_total == 0 ? -1 : bytes_written_total;
}
bytes_written_total += bytes_written_now;
char_data += bytes_written_now;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index b844b9f..e01e39b 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -23,9 +23,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
+#include <liblp/builder.h>
#include <liblp/liblp.h>
#include "fastboot_device.h"
@@ -35,7 +37,9 @@
using android::base::unique_fd;
using android::hardware::boot::V1_0::Slot;
-static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
+namespace {
+
+bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
std::optional<std::string> path = FindPhysicalPartition(name);
if (!path) {
return false;
@@ -44,28 +48,31 @@
return true;
}
-static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
- PartitionHandle* handle) {
- std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name,
+ PartitionHandle* handle) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
if (!path) {
return false;
}
- uint32_t slot_number = SlotNumberForSlotSuffix(slot);
std::string dm_path;
- if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
- LOG(ERROR) << "Could not map partition: " << name;
+ if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+ LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
- auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
+ auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
+} // namespace
+
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
// We prioritize logical partitions over physical ones, and do this
// consistently for other partition operations (like getvar:partition-size).
- if (LogicalPartitionExists(name, device->GetCurrentSlot())) {
- if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) {
+ if (LogicalPartitionExists(device, name)) {
+ if (!OpenLogicalPartition(device, name, handle)) {
return false;
}
} else if (!OpenPhysicalPartition(name, handle)) {
@@ -104,14 +111,14 @@
return nullptr;
}
-bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
- bool* is_zero_length) {
- auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {
+ std::string slot_suffix = GetSuperSlotSuffix(device, name);
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
if (!path) {
return false;
}
- uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
if (!metadata) {
return false;
@@ -154,12 +161,29 @@
}
}
- // Next get logical partitions.
- if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
- uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
- if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
- for (const auto& partition : metadata->partitions) {
- std::string partition_name = GetPartitionName(partition);
+ // Find metadata in each super partition (on retrofit devices, there will
+ // be two).
+ std::vector<std::unique_ptr<LpMetadata>> metadata_list;
+
+ uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ std::string super_name = fs_mgr_get_super_partition_name(current_slot);
+ if (auto metadata = ReadMetadata(super_name, current_slot)) {
+ metadata_list.emplace_back(std::move(metadata));
+ }
+
+ uint32_t other_slot = (current_slot == 0) ? 1 : 0;
+ std::string other_super = fs_mgr_get_super_partition_name(other_slot);
+ if (super_name != other_super) {
+ if (auto metadata = ReadMetadata(other_super, other_slot)) {
+ metadata_list.emplace_back(std::move(metadata));
+ }
+ }
+
+ for (const auto& metadata : metadata_list) {
+ for (const auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ if (std::find(partitions.begin(), partitions.end(), partition_name) ==
+ partitions.end()) {
partitions.emplace_back(partition_name);
}
}
@@ -175,3 +199,36 @@
}
return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
+
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
+ const android::fs_mgr::LpMetadata& metadata) {
+ size_t num_slots = 1;
+ auto boot_control_hal = device->boot_control_hal();
+ if (boot_control_hal) {
+ num_slots = boot_control_hal->getNumberSlots();
+ }
+
+ bool ok = true;
+ for (size_t i = 0; i < num_slots; i++) {
+ ok &= UpdatePartitionTable(super_name, metadata, i);
+ }
+ return ok;
+}
+
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {
+ // If the super partition does not have a slot suffix, this is not a
+ // retrofit device, and we should take the current slot.
+ std::string current_slot_suffix = device->GetCurrentSlot();
+ uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);
+ std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);
+ if (GetPartitionSlotSuffix(super_partition).empty()) {
+ return current_slot_suffix;
+ }
+
+ // Otherwise, infer the slot from the partition name.
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ if (!slot_suffix.empty()) {
+ return slot_suffix;
+ }
+ return current_slot_suffix;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index bb08f72..bfeeb74 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <liblp/liblp.h>
// Logical partitions are only mapped to a block device as needed, and
// immediately unmapped when no longer needed. In order to enforce this we
@@ -52,10 +53,20 @@
class FastbootDevice;
+// On normal devices, the super partition is always named "super". On retrofit
+// devices, the name must be derived from the partition name or current slot.
+// This helper assists in choosing the correct super for a given partition
+// name.
+std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name);
+
std::optional<std::string> FindPhysicalPartition(const std::string& name);
-bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
+bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
bool* is_zero_length = nullptr);
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
+
+// Update all copies of metadata.
+bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,
+ const android::fs_mgr::LpMetadata& metadata);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index cbd2856..130a3cf 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -24,7 +24,9 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
#include <healthhalutils/HealthHalUtils.h>
+#include <liblp/liblp.h>
#include "fastboot_device.h"
#include "flashing.h"
@@ -35,6 +37,7 @@
using ::android::hardware::fastboot::V1_0::FileSystemType;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using namespace android::fs_mgr;
constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -268,8 +271,7 @@
return true;
}
std::string partition_name = args[0] + slot_suffix;
- if (FindPhysicalPartition(partition_name) ||
- LogicalPartitionExists(partition_name, slot_suffix)) {
+ if (FindPhysicalPartition(partition_name) || LogicalPartitionExists(device, partition_name)) {
*message = "yes";
} else {
*message = "no";
@@ -286,8 +288,7 @@
// Zero-length partitions cannot be created through device-mapper, so we
// special case them here.
bool is_zero_length;
- if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
- is_zero_length) {
+ if (LogicalPartitionExists(device, args[0], &is_zero_length) && is_zero_length) {
*message = "0x0";
return true;
}
@@ -310,8 +311,7 @@
}
std::string partition_name = args[0];
- if (!FindPhysicalPartition(partition_name) &&
- !LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ if (!FindPhysicalPartition(partition_name) && !LogicalPartitionExists(device, partition_name)) {
*message = "Invalid partition";
return false;
}
@@ -360,7 +360,7 @@
// return "true", to be consistent with prefering to flash logical partitions
// over physical ones.
std::string partition_name = args[0];
- if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ if (LogicalPartitionExists(device, partition_name)) {
*message = "yes";
return true;
}
@@ -417,3 +417,10 @@
*message = android::base::GetProperty("ro.revision", "");
return true;
}
+
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& /* args */,
+ std::string* message) {
+ uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ *message = fs_mgr_get_super_partition_name(slot_number);
+ return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 59b71e8..015a4c5 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -59,6 +59,8 @@
std::string* message);
bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
+ std::string* message);
// Helpers for getvar all.
std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 3c6b1b7..8923f40 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -46,6 +46,7 @@
#include <chrono>
#include <functional>
#include <regex>
+#include <string>
#include <thread>
#include <utility>
#include <vector>
@@ -56,9 +57,9 @@
#include <android-base/parsenetaddress.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <build/version.h>
+#include <liblp/liblp.h>
#include <platform_tools_version.h>
#include <sparse/sparse.h>
#include <ziparchive/zip_archive.h>
@@ -78,6 +79,7 @@
using android::base::Split;
using android::base::Trim;
using android::base::unique_fd;
+using namespace std::string_literals;
static const char* serial = nullptr;
@@ -90,8 +92,9 @@
static int64_t target_sparse_limit = -1;
static unsigned g_base_addr = 0x10000000;
-static boot_img_hdr_v1 g_boot_img_hdr = {};
+static boot_img_hdr_v2 g_boot_img_hdr = {};
static std::string g_cmdline;
+static std::string g_dtb_path;
static bool g_disable_verity = false;
static bool g_disable_verification = false;
@@ -141,14 +144,13 @@
{ "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
{ "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
{ "product", "product.img", "product.sig", "product", true, ImageType::Normal },
- { "product_services",
- "product_services.img",
- "product_services.sig",
- "product_services",
- true, ImageType::Normal },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
{ "super", "super.img", "super.sig", "super", true, ImageType::Extra },
{ "system", "system.img", "system.sig", "system", false, ImageType::Normal },
+ { "system_ext",
+ "system_ext.img", "system_ext.sig",
+ "system_ext",
+ true, ImageType::Normal },
{ nullptr, "system_other.img", "system.sig", "system", true, ImageType::Normal },
{ "userdata", "userdata.img", "userdata.sig", "userdata", true, ImageType::Extra },
{ "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, ImageType::BootCritical },
@@ -162,9 +164,17 @@
// clang-format on
};
-static std::string find_item_given_name(const std::string& img_name) {
+static char* get_android_product_out() {
char* dir = getenv("ANDROID_PRODUCT_OUT");
if (dir == nullptr || dir[0] == '\0') {
+ return nullptr;
+ }
+ return dir;
+}
+
+static std::string find_item_given_name(const std::string& img_name) {
+ char* dir = get_android_product_out();
+ if (!dir) {
die("ANDROID_PRODUCT_OUT not set");
}
return std::string(dir) + "/" + img_name;
@@ -377,17 +387,22 @@
" Format a flash partition.\n"
" set_active SLOT Set the active slot.\n"
" oem [COMMAND...] Execute OEM-specific command.\n"
+ " gsi wipe|disable Wipe or disable a GSI installation (fastbootd only).\n"
+ " wipe-super [SUPER_EMPTY] Wipe the super partition. This will reset it to\n"
+ " contain an empty set of default dynamic partitions.\n"
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
" Download and boot kernel from RAM.\n"
" flash:raw PARTITION KERNEL [RAMDISK [SECOND]]\n"
" Create boot image and flash it.\n"
+ " --dtb DTB Specify path to DTB for boot image header version 2.\n"
" --cmdline CMDLINE Override kernel command line.\n"
" --base ADDRESS Set kernel base address (default: 0x10000000).\n"
" --kernel-offset Set kernel offset (default: 0x00008000).\n"
" --ramdisk-offset Set ramdisk offset (default: 0x01000000).\n"
" --tags-offset Set tags offset (default: 0x00000100).\n"
+ " --dtb-offset Set dtb offset (default: 0x01100000).\n"
" --page-size BYTES Set flash page size (default: 2048).\n"
" --header-version VERSION Set boot image header version.\n"
" --os-version MAJOR[.MINOR[.PATCH]]\n"
@@ -408,6 +423,7 @@
" -s SERIAL Specify a USB device.\n"
" -s tcp|udp:HOST[:PORT] Specify a network device.\n"
" -S SIZE[K|M|G] Break into sparse files no larger than SIZE.\n"
+ " --force Force a flash operation that may be unsafe.\n"
" --slot SLOT Use SLOT; 'all' for both slots, 'other' for\n"
" non-current slot (default: current active slot).\n"
" --set-active[=SLOT] Sets the active slot before rebooting.\n"
@@ -436,12 +452,12 @@
}
// Is this actually a boot image?
- if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
+ if (kernel_data.size() < sizeof(boot_img_hdr_v2)) {
die("cannot load '%s': too short", kernel.c_str());
}
if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if (!g_cmdline.empty()) {
- bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
+ bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(kernel_data.data()), g_cmdline);
}
if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
@@ -462,23 +478,33 @@
die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
}
}
+
+ std::vector<char> dtb_data;
+ if (!g_dtb_path.empty()) {
+ if (g_boot_img_hdr.header_version < 2) {
+ die("Argument dtb not supported for boot image header version %d\n",
+ g_boot_img_hdr.header_version);
+ }
+ if (!ReadFileToVector(g_dtb_path, &dtb_data)) {
+ die("cannot load '%s': %s", g_dtb_path.c_str(), strerror(errno));
+ }
+ }
+
fprintf(stderr,"creating boot image...\n");
std::vector<char> out;
- boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
- g_base_addr, g_boot_img_hdr, &out);
+ boot_img_hdr_v2* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+ dtb_data, g_base_addr, g_boot_img_hdr, &out);
if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
-
return out;
}
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipString zip_entry_name(entry_name.c_str());
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
@@ -588,9 +614,8 @@
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipString zip_entry_name(entry_name);
ZipEntry zip_entry;
- if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
+ if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
@@ -1097,6 +1122,14 @@
return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
}
+static bool is_retrofit_device() {
+ std::string value;
+ if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
+ return false;
+ }
+ return android::base::StartsWith(value, "system_");
+}
+
static void do_flash(const char* pname, const char* fname) {
struct fastboot_buffer buf;
@@ -1129,25 +1162,6 @@
return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
}
-static bool if_partition_exists(const std::string& partition, const std::string& slot) {
- std::string has_slot;
- std::string partition_name = partition;
-
- if (fb->GetVar("has-slot:" + partition, &has_slot) == fastboot::SUCCESS && has_slot == "yes") {
- if (slot == "") {
- std::string current_slot = get_current_slot();
- if (current_slot == "") {
- die("Failed to identify current slot");
- }
- partition_name += "_" + current_slot;
- } else {
- partition_name += "_" + slot;
- }
- }
- std::string partition_size;
- return fb->GetVar("partition-size:" + partition_name, &partition_size) == fastboot::SUCCESS;
-}
-
static void reboot_to_userspace_fastboot() {
fb->RebootTo("fastboot");
@@ -1158,6 +1172,14 @@
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
fb->set_transport(open_device());
+
+ if (!is_userspace_fastboot()) {
+ die("Failed to boot into userspace fastboot; one or more components might be unbootable.");
+ }
+
+ // Reset target_sparse_limit after reboot to userspace fastboot. Max
+ // download sizes may differ in bootloader and fastbootd.
+ target_sparse_limit = -1;
}
class ImageSource {
@@ -1200,6 +1222,15 @@
void FlashAllTool::Flash() {
DumpInfo();
CheckRequirements();
+
+ // Change the slot first, so we boot into the correct recovery image when
+ // using fastbootd.
+ if (slot_override_ == "all") {
+ set_active("a");
+ } else {
+ set_active(slot_override_);
+ }
+
DetermineSecondarySlot();
CollectImages();
@@ -1223,12 +1254,6 @@
// Flash OS images, resizing logical partitions as needed.
FlashImages(os_images_);
-
- if (slot_override_ == "all") {
- set_active("a");
- } else {
- set_active(slot_override_);
- }
}
void FlashAllTool::CheckRequirements() {
@@ -1243,7 +1268,7 @@
if (skip_secondary_) {
return;
}
- if (slot_override_ != "") {
+ if (slot_override_ != "" && slot_override_ != "all") {
secondary_slot_ = get_other_slot(slot_override_);
} else {
secondary_slot_ = get_other_slot();
@@ -1304,10 +1329,6 @@
}
void FlashAllTool::UpdateSuperPartition() {
- if (!if_partition_exists("super", "")) {
- return;
- }
-
int fd = source_.OpenFile("super_empty.img");
if (fd < 0) {
return;
@@ -1315,16 +1336,31 @@
if (!is_userspace_fastboot()) {
reboot_to_userspace_fastboot();
}
- if (!is_userspace_fastboot()) {
- die("Failed to boot into userspace; one or more components might be unbootable.");
- }
- fb->Download("super", fd, get_file_size(fd));
- std::string command = "update-super:super";
+ std::string super_name;
+ if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+ super_name = "super";
+ }
+ fb->Download(super_name, fd, get_file_size(fd));
+
+ std::string command = "update-super:" + super_name;
if (wipe_) {
command += ":wipe";
}
fb->RawCommand(command, "Updating super partition");
+
+ // Retrofit devices have two super partitions, named super_a and super_b.
+ // On these devices, secondary slots must be flashed as physical
+ // partitions (otherwise they would not mount on first boot). To enforce
+ // this, we delete any logical partitions for the "other" slot.
+ if (is_retrofit_device()) {
+ for (const auto& [image, slot] : os_images_) {
+ std::string partition_name = image->part_name + "_"s + slot;
+ if (image->IsSecondary() && is_logical(partition_name)) {
+ fb->DeletePartition(partition_name);
+ }
+ }
+ }
}
class ZipImageSource final : public ImageSource {
@@ -1374,7 +1410,7 @@
int LocalImageSource::OpenFile(const std::string& name) const {
auto path = find_item_given_name(name);
- return open(path.c_str(), O_RDONLY);
+ return open(path.c_str(), O_RDONLY | O_BINARY);
}
static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
@@ -1480,15 +1516,13 @@
fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
return;
}
- fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
- partition_type.c_str());
- return;
+ die("Formatting is not supported for file system with type '%s'.",
+ partition_type.c_str());
}
int64_t size;
if (!android::base::ParseInt(partition_size, &size)) {
- fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
- return;
+ die("Couldn't parse partition size '%s'.", partition_size.c_str());
}
unsigned eraseBlkSize, logicalBlkSize;
@@ -1498,17 +1532,14 @@
if (fs_generator_generate(gen, output.path, size, initial_dir,
eraseBlkSize, logicalBlkSize)) {
die("Cannot generate image for %s", partition.c_str());
- return;
}
fd.reset(open(output.path, O_RDONLY));
if (fd == -1) {
- fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
- return;
+ die("Cannot open generated image: %s", strerror(errno));
}
if (!load_buf_fd(fd.release(), &buf)) {
- fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
- return;
+ die("Cannot read image: %s", strerror(errno));
}
flash_buf(partition, &buf);
return;
@@ -1519,6 +1550,107 @@
if (errMsg) fprintf(stderr, "%s", errMsg);
}
fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+ if (!skip_if_not_supported) {
+ die("Command failed");
+ }
+}
+
+static bool should_flash_in_userspace(const std::string& partition_name) {
+ if (!get_android_product_out()) {
+ return false;
+ }
+ auto path = find_item_given_name("super_empty.img");
+ if (path.empty() || access(path.c_str(), R_OK)) {
+ return false;
+ }
+ auto metadata = android::fs_mgr::ReadFromImageFile(path);
+ if (!metadata) {
+ return false;
+ }
+ for (const auto& partition : metadata->partitions) {
+ auto candidate = android::fs_mgr::GetPartitionName(partition);
+ if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+ // On retrofit devices, we don't know if, or whether, the A or B
+ // slot has been flashed for dynamic partitions. Instead we add
+ // both names to the list as a conservative guess.
+ if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
+ return true;
+ }
+ } else if (candidate == partition_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
+ std::string* message) {
+ auto super_device = GetMetadataSuperBlockDevice(metadata);
+ auto block_size = metadata.geometry.logical_block_size;
+ auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);
+
+ if (super_bdev_name != "super") {
+ // retrofit devices do not allow flashing to the retrofit partitions,
+ // so enable it if we can.
+ fb->RawCommand("oem allow-flash-super");
+ }
+
+ // Note: do not use die() in here, since we want TemporaryDir's destructor
+ // to be called.
+ TemporaryDir temp_dir;
+
+ bool ok;
+ if (metadata.block_devices.size() > 1) {
+ ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
+ } else {
+ auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+ ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
+ }
+ if (!ok) {
+ *message = "Could not generate a flashable super image file";
+ return false;
+ }
+
+ for (const auto& block_device : metadata.block_devices) {
+ auto partition = android::fs_mgr::GetBlockDevicePartitionName(block_device);
+ bool force_slot = !!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED);
+
+ std::string image_name;
+ if (metadata.block_devices.size() > 1) {
+ image_name = "super_" + partition + ".img";
+ } else {
+ image_name = partition + ".img";
+ }
+
+ auto image_path = temp_dir.path + "/"s + image_name;
+ auto flash = [&](const std::string& partition_name) {
+ do_flash(partition_name.c_str(), image_path.c_str());
+ };
+ do_for_partitions(partition, slot, flash, force_slot);
+
+ unlink(image_path.c_str());
+ }
+ return true;
+}
+
+static void do_wipe_super(const std::string& image, const std::string& slot_override) {
+ if (access(image.c_str(), R_OK) != 0) {
+ die("Could not read image: %s", image.c_str());
+ }
+ auto metadata = android::fs_mgr::ReadFromImageFile(image);
+ if (!metadata) {
+ die("Could not parse image: %s", image.c_str());
+ }
+
+ auto slot = slot_override;
+ if (slot.empty()) {
+ slot = get_current_slot();
+ }
+
+ std::string message;
+ if (!wipe_super(*metadata.get(), slot, &message)) {
+ die(message);
+ }
}
int FastBootTool::Main(int argc, char* argv[]) {
@@ -1531,6 +1663,7 @@
bool wants_set_active = false;
bool skip_secondary = false;
bool set_fbe_marker = false;
+ bool force_flash = false;
int longindex;
std::string slot_override;
std::string next_active;
@@ -1540,12 +1673,14 @@
g_boot_img_hdr.second_addr = 0x00f00000;
g_boot_img_hdr.tags_addr = 0x00000100;
g_boot_img_hdr.page_size = 2048;
+ g_boot_img_hdr.dtb_addr = 0x01100000;
const struct option longopts[] = {
{"base", required_argument, 0, 0},
{"cmdline", required_argument, 0, 0},
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
+ {"force", no_argument, 0, 0},
{"header-version", required_argument, 0, 0},
{"help", no_argument, 0, 'h'},
{"kernel-offset", required_argument, 0, 0},
@@ -1558,6 +1693,8 @@
{"skip-secondary", no_argument, 0, 0},
{"slot", required_argument, 0, 0},
{"tags-offset", required_argument, 0, 0},
+ {"dtb", required_argument, 0, 0},
+ {"dtb-offset", required_argument, 0, 0},
{"unbuffered", no_argument, 0, 0},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 0},
@@ -1581,8 +1718,12 @@
g_disable_verification = true;
} else if (name == "disable-verity") {
g_disable_verity = true;
+ } else if (name == "force") {
+ force_flash = true;
} else if (name == "header-version") {
g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
+ } else if (name == "dtb") {
+ g_dtb_path = optarg;
} else if (name == "kernel-offset") {
g_boot_img_hdr.kernel_addr = strtoul(optarg, 0, 16);
} else if (name == "os-patch-level") {
@@ -1600,6 +1741,8 @@
skip_secondary = true;
} else if (name == "slot") {
slot_override = optarg;
+ } else if (name == "dtb-offset") {
+ g_boot_img_hdr.dtb_addr = strtoul(optarg, 0, 16);
} else if (name == "tags-offset") {
g_boot_img_hdr.tags_addr = strtoul(optarg, 0, 16);
} else if (name == "unbuffered") {
@@ -1735,7 +1878,7 @@
auto format = [&](const std::string& partition) {
fb_perform_format(partition, 0, type_override, size_override, "");
};
- do_for_partitions(partition.c_str(), slot_override, format, true);
+ do_for_partitions(partition, slot_override, format, true);
} else if (command == "signature") {
std::string filename = next_arg(&args);
std::vector<char> data;
@@ -1779,7 +1922,6 @@
if (!args.empty()) ramdisk = next_arg(&args);
std::string second_stage;
if (!args.empty()) second_stage = next_arg(&args);
-
auto data = LoadBootableImage(kernel, ramdisk, second_stage);
fb->Download("boot.img", data);
fb->Boot();
@@ -1795,9 +1937,19 @@
if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
auto flash = [&](const std::string &partition) {
+ if (should_flash_in_userspace(partition) && !is_userspace_fastboot() &&
+ !force_flash) {
+ die("The partition you are trying to flash is dynamic, and "
+ "should be flashed via fastbootd. Please run:\n"
+ "\n"
+ " fastboot reboot fastboot\n"
+ "\n"
+ "And try again. If you are intentionally trying to "
+ "overwrite a fixed partition, use --force.");
+ }
do_flash(partition.c_str(), fname.c_str());
};
- do_for_partitions(pname.c_str(), slot_override, flash, true);
+ do_for_partitions(pname, slot_override, flash, true);
} else if (command == "flash:raw") {
std::string partition = next_arg(&args);
std::string kernel = next_arg(&args);
@@ -1868,6 +2020,23 @@
std::string partition = next_arg(&args);
std::string size = next_arg(&args);
fb->ResizePartition(partition, size);
+ } else if (command == "gsi") {
+ std::string arg = next_arg(&args);
+ if (arg == "wipe") {
+ fb->RawCommand("gsi:wipe", "wiping GSI");
+ } else if (arg == "disable") {
+ fb->RawCommand("gsi:disable", "disabling GSI");
+ } else {
+ syntax_error("expected 'wipe' or 'disable'");
+ }
+ } else if (command == "wipe-super") {
+ std::string image;
+ if (args.empty()) {
+ image = find_item_given_name("super_empty.img");
+ } else {
+ image = next_arg(&args);
+ }
+ do_wipe_super(image, slot_override);
} else {
syntax_error("unknown command %s", command.c_str());
}
@@ -1905,8 +2074,7 @@
fb->RebootTo("recovery");
fb->WaitForDisconnect();
} else if (wants_reboot_fastboot) {
- fb->RebootTo("fastboot");
- fb->WaitForDisconnect();
+ reboot_to_userspace_fastboot();
}
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 65a5247..fea0a77 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -403,7 +403,7 @@
RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
int* dsize) {
char status[FB_RESPONSE_SZ + 1];
- auto start = std::chrono::system_clock::now();
+ auto start = std::chrono::steady_clock::now();
auto set_response = [response](std::string s) {
if (response) *response = std::move(s);
@@ -414,7 +414,7 @@
// erase response
set_response("");
- while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
+ while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
int r = transport_->Read(status, FB_RESPONSE_SZ);
if (r < 0) {
error_ = ErrnoStr("Status read failed");
@@ -427,6 +427,11 @@
std::string tmp = input.substr(strlen("INFO"));
info_(tmp);
add_info(std::move(tmp));
+ // We may receive one or more INFO packets during long operations,
+ // e.g. flash/erase if they are back by slow media like NAND/NOR
+ // flash. In that case, reset the timer since it's not a real
+ // timeout.
+ start = std::chrono::steady_clock::now();
} else if (android::base::StartsWith(input, "OKAY")) {
set_response(input.substr(strlen("OKAY")));
return SUCCESS;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index fc6a16b..8c0aa6b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -172,13 +172,8 @@
mkf2fs_args.push_back("-S");
std::string size_str = std::to_string(partSize);
mkf2fs_args.push_back(size_str.c_str());
- mkf2fs_args.push_back("-f");
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("encrypt");
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("quota");
- mkf2fs_args.push_back("-O");
- mkf2fs_args.push_back("verity");
+ mkf2fs_args.push_back("-g");
+ mkf2fs_args.push_back("android");
mkf2fs_args.push_back(fileName);
mkf2fs_args.push_back(nullptr);
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 301534b..277cc3a 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -26,6 +26,9 @@
"libadb_host",
"libtinyxml2",
"libsparse",
+ "liblp",
+ "libcrypto",
+ "libext4_utils",
],
// Static libs (libfastboot2) shared library dependencies are not transitively included
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 280cfb6..bc13a8c 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -55,9 +55,11 @@
#include "test_utils.h"
#include "usb_transport_sniffer.h"
+using namespace std::literals::chrono_literals;
+
namespace fastboot {
-int FastBootTest::MatchFastboot(usb_ifc_info* info, const char* local_serial) {
+int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {
if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
return -1;
}
@@ -66,8 +68,8 @@
// require matching serial number or device path if requested
// at the command line with the -s option.
- if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
- strcmp(local_serial, info->device_path) != 0))
+ if (!local_serial.empty() && local_serial != info->serial_number &&
+ local_serial != info->device_path)
return -1;
return 0;
}
@@ -111,7 +113,9 @@
ASSERT_TRUE(UsbStillAvailible()); // The device disconnected
}
- const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return MatchFastboot(info, device_serial);
+ };
for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
if (usb)
@@ -128,10 +132,14 @@
ASSERT_EQ(device_path, cb_scratch); // The path can not change
}
fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ // No error checking since non-A/B devices may not support the command
+ fb->GetVar("current-slot", &initial_slot);
}
void FastBootTest::TearDown() {
EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+ // No error checking since non-A/B devices may not support the command
+ fb->SetActive(initial_slot);
TearDownSerial();
@@ -159,6 +167,28 @@
}
}
+void FastBootTest::ReconnectFastbootDevice() {
+ fb.reset();
+ transport.reset();
+ while (UsbStillAvailible())
+ ;
+ printf("WAITING FOR DEVICE\n");
+ // Need to wait for device
+ const auto matcher = [](usb_ifc_info* info) -> int {
+ return MatchFastboot(info, device_serial);
+ };
+ while (!transport) {
+ std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+ if (usb) {
+ transport = std::unique_ptr<UsbTransportSniffer>(
+ new UsbTransportSniffer(std::move(usb), serial_port));
+ }
+ std::this_thread::sleep_for(1s);
+ }
+ device_path = cb_scratch;
+ fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+}
+
void FastBootTest::SetLockState(bool unlock, bool assert_change) {
if (!fb) {
return;
@@ -197,25 +227,8 @@
std::string cmd = unlock ? "unlock" : "lock";
ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
<< "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
- fb.reset();
- transport.reset();
printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
- while (UsbStillAvailible())
- ; // Wait for disconnect
- printf("WAITING FOR DEVICE");
- // Need to wait for device
- const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
- while (!transport) {
- std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
- if (usb) {
- transport = std::unique_ptr<UsbTransportSniffer>(
- new UsbTransportSniffer(std::move(usb), serial_port));
- }
- std::this_thread::sleep_for(std::chrono::milliseconds(1000));
- putchar('.');
- }
- device_path = cb_scratch;
- fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+ ReconnectFastbootDevice();
if (assert_change) {
ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
ASSERT_EQ(resp, unlock ? "yes" : "no")
@@ -227,7 +240,9 @@
std::string FastBootTest::device_path = "";
std::string FastBootTest::cb_scratch = "";
+std::string FastBootTest::initial_slot = "";
int FastBootTest::serial_port = 0;
+std::string FastBootTest::device_serial = "";
template <bool UNLOCKED>
void ModeTest<UNLOCKED>::SetUp() {
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index e0f829e..c71c897 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -43,11 +43,13 @@
class FastBootTest : public testing::Test {
public:
static int serial_port;
+ static std::string device_serial;
static constexpr int MAX_USB_TRIES = 10;
- static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
+ static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = "");
bool UsbStillAvailible();
bool UserSpaceFastboot();
+ void ReconnectFastbootDevice();
protected:
RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
@@ -69,6 +71,7 @@
// This is an annoying hack
static std::string cb_scratch;
static std::string device_path;
+ static std::string initial_slot;
};
template <bool UNLOCKED>
@@ -86,6 +89,7 @@
// differently
class BasicFunctionality : public ModeTest<true> {};
class Conformance : public ModeTest<true> {};
+class LogicalPartitionCompliance : public ModeTest<true> {};
class UnlockPermissions : public ModeTest<true> {};
class LockPermissions : public ModeTest<false> {};
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 479a06a..a1d69d2 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -162,7 +162,7 @@
// Test that USB even works
TEST(USBFunctionality, USBConnect) {
const auto matcher = [](usb_ifc_info* info) -> int {
- return FastBootTest::MatchFastboot(info, nullptr);
+ return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
Transport* transport = nullptr;
for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
@@ -177,6 +177,116 @@
}
}
+// Test commands related to super partition
+TEST_F(LogicalPartitionCompliance, SuperPartition) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ std::string partition_type;
+ // getvar partition-type:super must fail for retrofit devices because the
+ // partition does not exist.
+ if (fb->GetVar("partition-type:super", &partition_type) == SUCCESS) {
+ std::string is_logical;
+ EXPECT_EQ(fb->GetVar("is-logical:super", &is_logical), SUCCESS)
+ << "getvar is-logical:super failed";
+ EXPECT_EQ(is_logical, "no") << "super must not be a logical partition";
+ std::string super_name;
+ EXPECT_EQ(fb->GetVar("super-partition-name", &super_name), SUCCESS)
+ << "'getvar super-partition-name' failed";
+ EXPECT_EQ(super_name, "super") << "'getvar super-partition-name' must return 'super' for "
+ "device with a super partition";
+ }
+}
+
+// Test 'fastboot getvar is-logical'
+TEST_F(LogicalPartitionCompliance, GetVarIsLogical) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ std::string has_slot;
+ EXPECT_EQ(fb->GetVar("has-slot:system", &has_slot), SUCCESS) << "getvar has-slot:system failed";
+ std::string is_logical_cmd_system = "is-logical:system";
+ std::string is_logical_cmd_vendor = "is-logical:vendor";
+ std::string is_logical_cmd_boot = "is-logical:boot";
+ if (has_slot == "yes") {
+ std::string current_slot;
+ ASSERT_EQ(fb->GetVar("current-slot", ¤t_slot), SUCCESS)
+ << "getvar current-slot failed";
+ std::string slot_suffix = "_" + current_slot;
+ is_logical_cmd_system += slot_suffix;
+ is_logical_cmd_vendor += slot_suffix;
+ is_logical_cmd_boot += slot_suffix;
+ }
+ std::string is_logical;
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_system, &is_logical), SUCCESS)
+ << "system must be a logical partition";
+ EXPECT_EQ(is_logical, "yes");
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_vendor, &is_logical), SUCCESS)
+ << "vendor must be a logical partition";
+ EXPECT_EQ(is_logical, "yes");
+ EXPECT_EQ(fb->GetVar(is_logical_cmd_boot, &is_logical), SUCCESS)
+ << "boot must not be logical partition";
+ EXPECT_EQ(is_logical, "no");
+}
+
+TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+ // Test 'fastboot reboot bootloader' from fastbootd
+ fb->RebootTo("bootloader");
+
+ // Test fastboot reboot fastboot from bootloader
+ ReconnectFastbootDevice();
+ ASSERT_FALSE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+ fb->RebootTo("fastboot");
+
+ ReconnectFastbootDevice();
+ ASSERT_TRUE(UserSpaceFastboot());
+}
+
+// Testing creation/resize/delete of logical partitions
+TEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {
+ ASSERT_TRUE(UserSpaceFastboot());
+ std::string test_partition_name = "test_partition";
+ std::string slot_count;
+ // Add suffix to test_partition_name if device is slotted.
+ EXPECT_EQ(fb->GetVar("slot-count", &slot_count), SUCCESS) << "getvar slot-count failed";
+ int32_t num_slots = strtol(slot_count.c_str(), nullptr, 10);
+ if (num_slots > 0) {
+ std::string current_slot;
+ EXPECT_EQ(fb->GetVar("current-slot", ¤t_slot), SUCCESS)
+ << "getvar current-slot failed";
+ std::string slot_suffix = "_" + current_slot;
+ test_partition_name += slot_suffix;
+ }
+
+ GTEST_LOG_(INFO) << "Testing 'fastboot create-logical-partition' command";
+ EXPECT_EQ(fb->CreatePartition(test_partition_name, "0"), SUCCESS)
+ << "create-logical-partition failed";
+ GTEST_LOG_(INFO) << "Testing 'fastboot resize-logical-partition' command";
+ EXPECT_EQ(fb->ResizePartition(test_partition_name, "4096"), SUCCESS)
+ << "resize-logical-partition failed";
+ std::vector<char> buf(4096);
+
+ GTEST_LOG_(INFO) << "Flashing a logical partition..";
+ EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
+ << "flash logical -partition failed";
+ GTEST_LOG_(INFO) << "Rebooting to bootloader mode";
+ // Reboot to bootloader mode and attempt to flash the logical partitions
+ fb->RebootTo("bootloader");
+
+ ReconnectFastbootDevice();
+ ASSERT_FALSE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Attempt to flash a logical partition..";
+ EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), DEVICE_FAIL)
+ << "flash logical partition must fail in bootloader";
+ GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
+ fb->RebootTo("fastboot");
+
+ ReconnectFastbootDevice();
+ ASSERT_TRUE(UserSpaceFastboot());
+ GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
+ EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
+ << "delete logical-partition failed";
+}
+
// Conformance tests
TEST_F(Conformance, GetVar) {
std::string product;
@@ -281,7 +391,7 @@
std::vector<std::string> vars;
EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
- for (const auto s : vars) {
+ for (const auto& s : vars) {
EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
<< "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
}
@@ -316,7 +426,7 @@
EXPECT_GT(parts.size(), 0)
<< "getvar:all did not report any partition-size: through INFO responses";
std::set<std::string> allowed{"ext4", "f2fs", "raw"};
- for (const auto p : parts) {
+ for (const auto& p : parts) {
EXPECT_GE(std::get<1>(p), 0);
std::string part(std::get<0>(p));
std::set<std::string> allowed{"ext4", "f2fs", "raw"};
@@ -355,7 +465,7 @@
if (num_slots > 0) {
EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
- for (const auto p : parts) {
+ for (const auto& p : parts) {
std::string part(std::get<0>(p));
std::regex reg("([[:graph:]]*)_([[:lower:]])");
std::smatch sm;
@@ -378,7 +488,7 @@
}
}
// Ensure each partition has the correct slot suffix
- for (const auto iter : part_slots) {
+ for (const auto& iter : part_slots) {
const std::set<char>& char_set = iter.second;
std::string chars;
for (char c : char_set) {
@@ -572,7 +682,7 @@
std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
std::string resp;
- for (const auto tup : parts) {
+ for (const auto& tup : parts) {
EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
<< "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
<< "' in locked mode";
@@ -585,7 +695,7 @@
std::vector<std::tuple<std::string, uint64_t>> parts;
EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
std::string resp;
- for (const auto tup : parts) {
+ for (const auto& tup : parts) {
EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
<< "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
<< "' in locked mode";
@@ -601,7 +711,7 @@
EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
- for (const auto tup : parts) {
+ for (const auto& tup : parts) {
std::string part(std::get<0>(tup));
std::regex reg("([[:graph:]]*)_([[:lower:]])");
std::smatch sm;
@@ -1554,12 +1664,12 @@
void GenerateXmlTests(const extension::Configuration& config) {
// Build the getvar tests
- for (const auto it : config.getvars) {
+ for (const auto& it : config.getvars) {
GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
}
// Build the partition tests, to interface with gtest we need to do it this way
- for (const auto it : config.partitions) {
+ for (const auto& it : config.partitions) {
const auto tup = std::make_tuple(it.first, it.second);
PARTITION_XML_TESTS.push_back(tup); // All partitions
@@ -1581,7 +1691,7 @@
// Build the packed tests, only useful if we have a hash
if (!config.checksum.empty()) {
- for (const auto it : config.packed) {
+ for (const auto& it : config.packed) {
for (const auto& test : it.second.tests) {
const auto tup = std::make_tuple(it.first, test);
if (test.expect == extension::OKAY) { // only testing the success case
@@ -1608,7 +1718,7 @@
}
// Build oem tests
- for (const auto it : config.oem) {
+ for (const auto& it : config.oem) {
auto oem_cmd = it.second;
for (const auto& t : oem_cmd.tests) {
OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
@@ -1641,10 +1751,14 @@
fastboot::GenerateXmlTests(fastboot::config);
}
+ if (args.find("serial") != args.end()) {
+ fastboot::FastBootTest::device_serial = args.at("serial");
+ }
+
setbuf(stdout, NULL); // no buffering
printf("<Waiting for Device>\n");
const auto matcher = [](usb_ifc_info* info) -> int {
- return fastboot::FastBootTest::MatchFastboot(info, nullptr);
+ return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
};
Transport* transport = nullptr;
while (!transport) {
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index ed02c4a..8a3c213 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -61,7 +61,7 @@
UInt8 bulkIn;
UInt8 bulkOut;
- IOUSBInterfaceInterface190 **interface;
+ IOUSBInterfaceInterface500** interface;
unsigned int zero_mask;
};
@@ -86,13 +86,13 @@
/** Try out all the interfaces and see if there's a match. Returns 0 on
* success, -1 on failure. */
-static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
+static int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {
IOReturn kr;
IOUSBFindInterfaceRequest request;
io_iterator_t iterator;
io_service_t usbInterface;
IOCFPlugInInterface **plugInInterface;
- IOUSBInterfaceInterface190 **interface = NULL;
+ IOUSBInterfaceInterface500** interface = NULL;
HRESULT result;
SInt32 score;
UInt8 interfaceNumEndpoints;
@@ -106,7 +106,7 @@
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
if (kr != 0) {
- ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
+ WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
return -1;
}
@@ -128,10 +128,10 @@
}
// Now create the interface interface for the interface
- result = (*plugInInterface)->QueryInterface(
- plugInInterface,
- CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
- (LPVOID*) &interface);
+ result = (*plugInInterface)
+ ->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),
+ (LPVOID*)&interface);
// No longer need the intermediate plugin
(*plugInInterface)->Release(plugInInterface);
@@ -270,7 +270,7 @@
static int try_device(io_service_t device, usb_handle *handle) {
kern_return_t kr;
IOCFPlugInInterface **plugin = NULL;
- IOUSBDeviceInterface182 **dev = NULL;
+ IOUSBDeviceInterface500** dev = NULL;
SInt32 score;
HRESULT result;
UInt8 serialIndex;
@@ -287,8 +287,8 @@
}
// Now create the device interface.
- result = (*plugin)->QueryInterface(plugin,
- CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
+ result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),
+ (LPVOID*)&dev);
if ((result != 0) || (dev == NULL)) {
ERR("Couldn't create a device interface (%08x)\n", (int) result);
goto error;
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index b00edb3..bf840f8 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -195,25 +195,28 @@
ssize_t WindowsUsbTransport::Read(void* data, size_t len) {
unsigned long time_out = 0;
unsigned long read = 0;
+ size_t count = 0;
int ret;
DBG("usb_read %zu\n", len);
if (nullptr != handle_) {
- while (1) {
- int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+ while (len > 0) {
+ size_t xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);
errno = GetLastError();
- DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
- if (ret) {
- return read;
- } else {
+ DBG("usb_read got: %lu, expected: %zu, errno: %d\n", read, xfer, errno);
+ if (ret == 0) {
// assume ERROR_INVALID_HANDLE indicates we are disconnected
if (errno == ERROR_INVALID_HANDLE)
usb_kick(handle_.get());
break;
}
- // else we timed out - try again
+ count += read;
+ len -= read;
+ data = (char*)data + read;
+
+ if (xfer != read || len == 0) return count;
}
} else {
DBG("usb_read NULL handle\n");
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index d02b37f..900d6ea 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -53,6 +53,10 @@
exit(EXIT_FAILURE);
}
+void die(const std::string& str) {
+ die("%s", str.c_str());
+}
+
void set_verbose() {
g_verbose = true;
}
diff --git a/fastboot/util.h b/fastboot/util.h
index 2535414..c719df2 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -15,4 +15,7 @@
// use the same attribute for compile-time format string checking.
void die(const char* fmt, ...) __attribute__((__noreturn__))
__attribute__((__format__(__printf__, 1, 2)));
+
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
+void die(const std::string& str) __attribute__((__noreturn__));
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3cce0e8..65f0eff 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -23,10 +23,6 @@
cflags: [
"-Wall",
"-Werror",
- "-Wno-unused-variable",
- ],
- cppflags: [
- "-std=gnu++1z",
],
}
@@ -39,13 +35,14 @@
export_include_dirs: ["include"],
include_dirs: ["system/vold"],
srcs: [
+ "file_wait.cpp",
"fs_mgr.cpp",
"fs_mgr_format.cpp",
"fs_mgr_verity.cpp",
- "fs_mgr_avb.cpp",
- "fs_mgr_avb_ops.cpp",
"fs_mgr_dm_linear.cpp",
"fs_mgr_overlayfs.cpp",
+ "fs_mgr_roots.cpp",
+ "fs_mgr_vendor_overlay.cpp",
],
shared_libs: [
"libbase",
@@ -60,10 +57,13 @@
],
static_libs: [
"libavb",
+ "libfs_avb",
"libfstab",
"libdm",
+ "libgsi",
],
export_static_lib_headers: [
+ "libfs_avb",
"libfstab",
"libdm",
],
@@ -73,6 +73,7 @@
whole_static_libs: [
"liblogwrap",
"libdm",
+ "libext2_uuid",
"libfstab",
],
cppflags: [
@@ -94,12 +95,58 @@
name: "libfstab",
vendor_available: true,
recovery_available: true,
+ host_supported: true,
defaults: ["fs_mgr_defaults"],
srcs: [
"fs_mgr_fstab.cpp",
"fs_mgr_boot_config.cpp",
"fs_mgr_slotselect.cpp",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
export_include_dirs: ["include_fstab"],
- header_libs: ["libbase_headers"],
+ header_libs: [
+ "libbase_headers",
+ "libgsi_headers",
+ ],
+}
+
+cc_binary {
+ name: "remount",
+ defaults: ["fs_mgr_defaults"],
+ static_libs: [
+ "libavb_user",
+ ],
+ shared_libs: [
+ "libbootloader_message",
+ "libbase",
+ "libcutils",
+ "libcrypto",
+ "libext4_utils",
+ "libfec",
+ "libfs_mgr",
+ "liblog",
+ "liblp",
+ "libselinux",
+ ],
+ header_libs: [
+ "libcutils_headers",
+ ],
+ srcs: [
+ "fs_mgr_remount.cpp",
+ ],
+ cppflags: [
+ "-DALLOW_ADBD_DISABLE_VERITY=0",
+ ],
+ product_variables: {
+ debuggable: {
+ cppflags: [
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
+ ],
+ },
+ },
}
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index 817a0b8..cbbd3bc 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,2 +1,3 @@
bowgotsai@google.com
+dvander@google.com
tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
new file mode 100644
index 0000000..fe2e052
--- /dev/null
+++ b/fs_mgr/README.overlayfs.md
@@ -0,0 +1,141 @@
+Android Overlayfs integration with adb remount
+==============================================
+
+Introduction
+------------
+
+Users working with userdebug or eng builds expect to be able to
+remount the system partition as read-write and then add or modify
+any number of files without reflashing the system image, which is
+understandably efficient for a development cycle.
+Limited memory systems that chose to use readonly filesystems like
+*squashfs*, or *Logical Resizable Android Partitions* which land
+system partition images right-sized, and with filesystem that have
+been deduped on the block level to compress the content; means that
+either a remount is not possible directly, or when done offers
+little or no utility because of remaining space limitations or
+support logistics.
+
+*Overlayfs* comes to the rescue for these debug scenarios, and logic
+will _automatically_ setup backing storage for a writable filesystem
+as an upper reference, and mount overtop the lower. These actions
+will be performed in the **adb disable-verity** and **adb remount**
+requests.
+
+Operations
+----------
+
+### Cookbook
+
+The typical action to utilize the remount facility is:
+
+ $ adb root
+ $ adb disable-verity
+ $ adb reboot
+ $ adb wait-for-device
+ $ adb root
+ $ adb remount
+
+Followed by one of the following:
+
+ $ adb stop
+ $ adb sync
+ $ adb start
+ $ adb reboot
+
+*or*
+
+ $ adb push <source> <destination>
+ $ adb reboot
+
+Note that the sequence above:
+
+ $ adb disable-verity
+ $ adb reboot
+
+*or*
+
+ $ adb remount
+
+can be replaced in both places with:
+
+ $ adb remount -R
+
+which will not reboot if everything is already prepared and ready
+to go.
+
+None of this changes if *overlayfs* needs to be engaged.
+The decisions whether to use traditional direct filesystem remount,
+or one wrapped by *overlayfs* is automatically determined based on
+a probe of the filesystem types and space remaining.
+
+### Backing Storage
+
+When *overlayfs* logic is feasible, it will use either the
+**/cache/overlay/** directory for non-A/B devices, or the
+**/mnt/scratch/overlay** directory for A/B devices that have
+access to *Logical Resizable Android Partitions*.
+The backing store is used as soon as possible in the boot
+process and can occur at first stage init, or at the
+mount_all init rc commands.
+
+This early as possible attachment of *overlayfs* means that
+*sepolicy* or *init* itself can also be pushed and used after
+the exec phases that accompany each stage.
+
+Caveats
+-------
+
+- Space used in the backing storage is on a file by file basis
+ and will require more space than if updated in place. As such
+ it is important to be mindful of any wasted space, for instance
+ **BOARD_<partition>IMAGE_PARTITION_RESERVED_SIZE** being defined
+ will have a negative impact on the overall right-sizing of images
+ and thus free dynamic partition space.
+- Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
+ with "*overlayfs: override_creds=off option bypass creator_cred*"
+ if kernel is 4.4 or higher.
+ The patch series is available on the upstream mailing list and the latest as
+ of Jul 24 2019 is https://lore.kernel.org/patchwork/patch/1104577/.
+ This patch adds an override_creds _mount_ option to overlayfs that
+ permits legacy behavior for systems that do not have overlapping
+ sepolicy rules, principals of least privilege, which is how Android behaves.
+- *adb enable-verity* will free up overlayfs and as a bonus the
+ device will be reverted pristine to before any content was updated.
+ Update engine does not take advantage of this, will perform a full OTA.
+- Update engine may not run if *fs_mgr_overlayfs_is_setup*() reports
+ true as adb remount overrides are incompatible with an OTA resources.
+- For implementation simplicity on retrofit dynamic partition devices,
+ take the whole alternate super (eg: if "*a*" slot, then the whole of
+ "*system_b*").
+ Since landing a filesystem on the alternate super physical device
+ without differentiating if it is setup to support logical or physical,
+ the alternate slot metadata and previous content will be lost.
+- If dynamic partitions runs out of space, resizing a logical
+ partition larger may fail because of the scratch partition.
+ If this happens, either fastboot flashall or adb enable-verity can
+ be used to clear scratch storage to permit the flash.
+ Then reinstate the overrides and continue.
+- File bugs or submit fixes for review.
+- There are other subtle caveats requiring complex logic to solve.
+ Have evaluated them as too complex or not worth the trouble, please
+ File a bug if a use case needs to be covered.
+ - The backing storage is treated fragile, if anything else has
+ issue with the space taken, the backing storage will be cleared
+ out and we reserve the right to not inform, if the layering
+ does not prevent any messaging.
+ - Space remaining threshold is hard coded. If 1% or more space
+ still remains, overlayfs will not be used, yet that amount of
+ space remaining is problematic.
+ - Flashing a partition via bootloader fastboot, as opposed to user
+ space fastbootd, is not detected, thus a partition may have
+ override content remaining. adb enable-verity to wipe.
+ - Space is limited, there is near unlimited space on userdata,
+ we have made an architectural decision to not utilize
+ /data/overlay/ at this time. Acquiring space to use for
+ backing remains an ongoing battle.
+ - First stage init, or ramdisk, can not be overriden.
+ - Backing storage will be discarded or ignored on errors, leading
+ to confusion. When debugging using **adb remount** it is
+ currently advised to confirm update is present after a reboot
+ to develop confidence.
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
new file mode 100644
index 0000000..cbf6845
--- /dev/null
+++ b/fs_mgr/file_wait.cpp
@@ -0,0 +1,235 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fs_mgr/file_wait.h>
+
+#include <limits.h>
+#if defined(__linux__)
+#include <poll.h>
+#include <sys/inotify.h>
+#endif
+#if defined(WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fs_mgr {
+
+using namespace std::literals;
+using android::base::unique_fd;
+
+bool PollForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+
+ while (true) {
+ if (!access(path.c_str(), F_OK) || errno != ENOENT) return true;
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
+ }
+}
+
+bool PollForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
+
+ while (true) {
+ if (access(path.c_str(), F_OK) && errno == ENOENT) return true;
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
+ }
+}
+
+#if defined(__linux__)
+class OneShotInotify {
+ public:
+ OneShotInotify(const std::string& path, uint32_t mask,
+ const std::chrono::milliseconds relative_timeout);
+
+ bool Wait();
+
+ private:
+ bool CheckCompleted();
+ int64_t RemainingMs() const;
+ bool ConsumeEvents();
+
+ enum class Result { Success, Timeout, Error };
+ Result WaitImpl();
+
+ unique_fd inotify_fd_;
+ std::string path_;
+ uint32_t mask_;
+ std::chrono::time_point<std::chrono::steady_clock> start_time_;
+ std::chrono::milliseconds relative_timeout_;
+ bool finished_;
+};
+
+OneShotInotify::OneShotInotify(const std::string& path, uint32_t mask,
+ const std::chrono::milliseconds relative_timeout)
+ : path_(path),
+ mask_(mask),
+ start_time_(std::chrono::steady_clock::now()),
+ relative_timeout_(relative_timeout),
+ finished_(false) {
+ // If the condition is already met, don't bother creating an inotify.
+ if (CheckCompleted()) return;
+
+ unique_fd inotify_fd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK));
+ if (inotify_fd < 0) {
+ PLOG(ERROR) << "inotify_init1 failed";
+ return;
+ }
+
+ std::string watch_path;
+ if (mask == IN_CREATE) {
+ watch_path = android::base::Dirname(path);
+ } else {
+ watch_path = path;
+ }
+ if (inotify_add_watch(inotify_fd, watch_path.c_str(), mask) < 0) {
+ PLOG(ERROR) << "inotify_add_watch failed";
+ return;
+ }
+
+ // It's possible the condition was met before the add_watch. Check for
+ // this and abort early if so.
+ if (CheckCompleted()) return;
+
+ inotify_fd_ = std::move(inotify_fd);
+}
+
+bool OneShotInotify::Wait() {
+ Result result = WaitImpl();
+ if (result == Result::Success) return true;
+ if (result == Result::Timeout) return false;
+
+ // Some kind of error with inotify occurred, so fallback to a poll.
+ std::chrono::milliseconds timeout(RemainingMs());
+ if (mask_ == IN_CREATE) {
+ return PollForFile(path_, timeout);
+ } else if (mask_ == IN_DELETE_SELF) {
+ return PollForFileDeleted(path_, timeout);
+ } else {
+ LOG(ERROR) << "Unknown inotify mask: " << mask_;
+ return false;
+ }
+}
+
+OneShotInotify::Result OneShotInotify::WaitImpl() {
+ // If the operation completed super early, we'll never have created an
+ // inotify instance.
+ if (finished_) return Result::Success;
+ if (inotify_fd_ < 0) return Result::Error;
+
+ while (true) {
+ auto remaining_ms = RemainingMs();
+ if (remaining_ms <= 0) return Result::Timeout;
+
+ struct pollfd event = {
+ .fd = inotify_fd_,
+ .events = POLLIN,
+ .revents = 0,
+ };
+ int rv = poll(&event, 1, static_cast<int>(remaining_ms));
+ if (rv <= 0) {
+ if (rv == 0 || errno == EINTR) {
+ continue;
+ }
+ PLOG(ERROR) << "poll for inotify failed";
+ return Result::Error;
+ }
+ if (event.revents & POLLERR) {
+ LOG(ERROR) << "error reading inotify for " << path_;
+ return Result::Error;
+ }
+
+ // Note that we don't bother checking what kind of event it is, since
+ // it's cheap enough to just see if the initial condition is satisified.
+ // If it's not, we consume all the events available and continue.
+ if (CheckCompleted()) return Result::Success;
+ if (!ConsumeEvents()) return Result::Error;
+ }
+}
+
+bool OneShotInotify::CheckCompleted() {
+ if (mask_ == IN_CREATE) {
+ finished_ = !access(path_.c_str(), F_OK) || errno != ENOENT;
+ } else if (mask_ == IN_DELETE_SELF) {
+ finished_ = access(path_.c_str(), F_OK) && errno == ENOENT;
+ } else {
+ LOG(ERROR) << "Unexpected mask: " << mask_;
+ }
+ return finished_;
+}
+
+bool OneShotInotify::ConsumeEvents() {
+ // According to the manpage, this is enough to read at least one event.
+ static constexpr size_t kBufferSize = sizeof(struct inotify_event) + NAME_MAX + 1;
+ char buffer[kBufferSize];
+
+ do {
+ ssize_t rv = TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer)));
+ if (rv <= 0) {
+ if (rv == 0 || errno == EAGAIN) {
+ return true;
+ }
+ PLOG(ERROR) << "read inotify failed";
+ return false;
+ }
+ } while (true);
+}
+
+int64_t OneShotInotify::RemainingMs() const {
+ auto remaining = (std::chrono::steady_clock::now() - start_time_);
+ auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
+ return (relative_timeout_ - elapsed).count();
+}
+#endif
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+ OneShotInotify inotify(path, IN_CREATE, relative_timeout);
+ return inotify.Wait();
+#else
+ return PollForFile(path, relative_timeout);
+#endif
+}
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {
+#if defined(__linux__)
+ OneShotInotify inotify(path, IN_DELETE_SELF, relative_timeout);
+ return inotify.Wait();
+#else
+ return PollForFileDeleted(path, relative_timeout);
+#endif
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ae2e2fe..7a0d019 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -20,6 +20,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
@@ -34,6 +35,7 @@
#include <unistd.h>
#include <functional>
+#include <map>
#include <memory>
#include <string>
#include <thread>
@@ -53,6 +55,8 @@
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_mgr/file_wait.h>
#include <fs_mgr_overlayfs.h>
#include <libdm/dm.h>
#include <liblp/metadata_format.h>
@@ -62,7 +66,6 @@
#include <log/log_properties.h>
#include <logwrap/logwrap.h>
-#include "fs_mgr_avb.h"
#include "fs_mgr_priv.h"
#define KEY_LOC_PROP "ro.crypto.keyfile.userdata"
@@ -77,12 +80,24 @@
#define ZRAM_CONF_DEV "/sys/block/zram0/disksize"
#define ZRAM_CONF_MCS "/sys/block/zram0/max_comp_streams"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
+
+#define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+using android::base::Basename;
+using android::base::Realpath;
+using android::base::StartsWith;
+using android::base::unique_fd;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+using namespace std::literals;
+
// record fs stat
enum FsStatFlags {
FS_STAT_IS_EXT4 = 0x0001,
@@ -95,38 +110,17 @@
FS_STAT_FULL_MOUNT_FAILED = 0x0100,
FS_STAT_E2FSCK_FAILED = 0x0200,
FS_STAT_E2FSCK_FS_FIXED = 0x0400,
- FS_STAT_EXT4_INVALID_MAGIC = 0x0800,
+ FS_STAT_INVALID_MAGIC = 0x0800,
FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
+ FS_STAT_ENABLE_VERITY_FAILED = 0x80000,
};
-// TODO: switch to inotify()
-bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout,
- FileWaitMode file_wait_mode) {
- auto start_time = std::chrono::steady_clock::now();
-
- while (true) {
- int rv = access(filename.c_str(), F_OK);
- if (file_wait_mode == FileWaitMode::Exists) {
- if (!rv || errno != ENOENT) return true;
- } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
- if (rv && errno == ENOENT) return true;
- }
-
- std::this_thread::sleep_for(50ms);
-
- auto now = std::chrono::steady_clock::now();
- auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
- if (time_elapsed > relative_timeout) return false;
- }
-}
-
-static void log_fs_stat(const char* blk_device, int fs_stat)
-{
+static void log_fs_stat(const std::string& blk_device, int fs_stat) {
if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
- std::string msg = android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device, fs_stat);
+ std::string msg =
+ android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
O_APPEND | O_CREAT, 0664)));
if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
@@ -138,6 +132,18 @@
return fs_type == "ext4" || fs_type == "ext3" || fs_type == "ext2";
}
+static bool is_f2fs(const std::string& fs_type) {
+ return fs_type == "f2fs";
+}
+
+static std::string realpath(const std::string& blk_device) {
+ std::string real_path;
+ if (!Realpath(blk_device, &real_path)) {
+ real_path = blk_device;
+ }
+ return real_path;
+}
+
static bool should_force_check(int fs_stat) {
return fs_stat &
(FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
@@ -146,20 +152,21 @@
FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
}
-static void check_fs(const char *blk_device, char *fs_type, char *target, int *fs_stat)
-{
+static void check_fs(const std::string& blk_device, const std::string& fs_type,
+ const std::string& target, int* fs_stat) {
int status;
int ret;
long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
- char tmpmnt_opts[64] = "errors=remount-ro";
- const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device};
- const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device};
+ auto tmpmnt_opts = "errors=remount-ro"s;
+ const char* e2fsck_argv[] = {E2FSCK_BIN, "-y", blk_device.c_str()};
+ const char* e2fsck_forced_argv[] = {E2FSCK_BIN, "-f", "-y", blk_device.c_str()};
+
+ if (*fs_stat & FS_STAT_INVALID_MAGIC) { // will fail, so do not try
+ return;
+ }
/* Check for the types of filesystems we know how to check */
if (is_extfs(fs_type)) {
- if (*fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { // will fail, so do not try
- return;
- }
/*
* First try to mount and unmount the filesystem. We do this because
* the kernel is more efficient than e2fsck in running the journal and
@@ -175,18 +182,19 @@
*/
if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) { // already tried if full mount failed
errno = 0;
- if (!strcmp(fs_type, "ext4")) {
+ if (fs_type == "ext4") {
// This option is only valid with ext4
- strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+ tmpmnt_opts += ",nomblk_io_submit";
}
- ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
+ ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,
+ tmpmnt_opts.c_str());
PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
<< ")=" << ret;
if (!ret) {
bool umounted = false;
int retry_count = 5;
while (retry_count-- > 0) {
- umounted = umount(target) == 0;
+ umounted = umount(target.c_str()) == 0;
if (umounted) {
LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
break;
@@ -209,18 +217,18 @@
* (e.g. recent SDK system images). Detect these and skip the check.
*/
if (access(E2FSCK_BIN, X_OK)) {
- LINFO << "Not running " << E2FSCK_BIN << " on " << blk_device
+ LINFO << "Not running " << E2FSCK_BIN << " on " << realpath(blk_device)
<< " (executable not in system image)";
} else {
- LINFO << "Running " << E2FSCK_BIN << " on " << blk_device;
+ LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
if (should_force_check(*fs_stat)) {
ret = android_fork_execvp_ext(
ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
- true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
} else {
ret = android_fork_execvp_ext(
ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
- LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), NULL, 0);
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
}
if (ret < 0) {
@@ -232,19 +240,21 @@
*fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
}
}
- } else if (!strcmp(fs_type, "f2fs")) {
- const char *f2fs_fsck_argv[] = {
- F2FS_FSCK_BIN,
- "-a",
- blk_device
- };
- LINFO << "Running " << F2FS_FSCK_BIN << " -a " << blk_device;
+ } else if (is_f2fs(fs_type)) {
+ const char* f2fs_fsck_argv[] = {F2FS_FSCK_BIN, "-a", blk_device.c_str()};
+ const char* f2fs_fsck_forced_argv[] = {F2FS_FSCK_BIN, "-f", blk_device.c_str()};
- ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv),
- const_cast<char **>(f2fs_fsck_argv),
- &status, true, LOG_KLOG | LOG_FILE,
- true, const_cast<char *>(FSCK_LOG_FILE),
- NULL, 0);
+ if (should_force_check(*fs_stat)) {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(f2fs_fsck_forced_argv), const_cast<char**>(f2fs_fsck_forced_argv), &status,
+ true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+ } else {
+ LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
+ ret = android_fork_execvp_ext(
+ ARRAY_SIZE(f2fs_fsck_argv), const_cast<char**>(f2fs_fsck_argv), &status, true,
+ LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+ }
if (ret < 0) {
/* No need to check for error in fork, we can't really handle it now */
LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
@@ -272,16 +282,17 @@
}
// Read the primary superblock from an ext4 filesystem. On failure return
-// false. If it's not an ext4 filesystem, also set FS_STAT_EXT4_INVALID_MAGIC.
-static bool read_ext4_superblock(const char* blk_device, struct ext4_super_block* sb, int* fs_stat) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC)));
+// false. If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
+static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
+ int* fs_stat) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
PERROR << "Failed to open '" << blk_device << "'";
return false;
}
- if (pread(fd, sb, sizeof(*sb), 1024) != sizeof(*sb)) {
+ if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {
PERROR << "Can't read '" << blk_device << "' superblock";
return false;
}
@@ -289,7 +300,7 @@
if (!is_ext4_superblock_valid(sb)) {
LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
// not a valid fs, tune2fs, fsck, and mount will all fail.
- *fs_stat |= FS_STAT_EXT4_INVALID_MAGIC;
+ *fs_stat |= FS_STAT_INVALID_MAGIC;
return false;
}
*fs_stat |= FS_STAT_IS_EXT4;
@@ -300,6 +311,17 @@
return true;
}
+// exported silent version of the above that just answer the question is_ext4
+bool fs_mgr_is_ext4(const std::string& blk_device) {
+ android::base::ErrnoRestorer restore;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) return false;
+ ext4_super_block sb;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;
+ if (!is_ext4_superblock_valid(&sb)) return false;
+ return true;
+}
+
// Some system images do not have tune2fs for licensing reasons.
// Detect these and skip running it.
static bool tune2fs_available(void) {
@@ -315,10 +337,10 @@
}
// Enable/disable quota support on the filesystem if needed.
-static void tune_quota(const char* blk_device, const struct fstab_rec* rec,
+static void tune_quota(const std::string& blk_device, const FstabEntry& entry,
const struct ext4_super_block* sb, int* fs_stat) {
bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
- bool want_quota = fs_mgr_is_quota(rec) != 0;
+ bool want_quota = entry.fs_mgr_flags.quota;
if (has_quota == want_quota) {
return;
@@ -330,7 +352,7 @@
return;
}
- const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device};
+ const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device.c_str()};
if (want_quota) {
LINFO << "Enabling quotas on " << blk_device;
@@ -351,16 +373,16 @@
}
// Set the number of reserved filesystem blocks if needed.
-static void tune_reserved_size(const char* blk_device, const struct fstab_rec* rec,
+static void tune_reserved_size(const std::string& blk_device, const FstabEntry& entry,
const struct ext4_super_block* sb, int* fs_stat) {
- if (!(rec->fs_mgr_flags & MF_RESERVEDSIZE)) {
+ if (entry.reserved_size == 0) {
return;
}
// The size to reserve is given in the fstab, but we won't reserve more
// than 2% of the filesystem.
const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;
- uint64_t reserved_blocks = rec->reserved_size / EXT4_BLOCK_SIZE(sb);
+ uint64_t reserved_blocks = entry.reserved_size / EXT4_BLOCK_SIZE(sb);
if (reserved_blocks > max_reserved_blocks) {
LWARNING << "Reserved blocks " << reserved_blocks << " is too large; "
@@ -383,7 +405,8 @@
auto reserved_blocks_str = std::to_string(reserved_blocks);
auto reserved_gid_str = std::to_string(AID_RESERVED_DISK);
const char* argv[] = {
- TUNE2FS_BIN, "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(), blk_device};
+ TUNE2FS_BIN, "-r", reserved_blocks_str.c_str(), "-g", reserved_gid_str.c_str(),
+ blk_device.c_str()};
if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
LERROR << "Failed to run " TUNE2FS_BIN " to set the number of reserved blocks on "
<< blk_device;
@@ -392,10 +415,10 @@
}
// Enable file-based encryption if needed.
-static void tune_encrypt(const char* blk_device, const struct fstab_rec* rec,
+static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
const struct ext4_super_block* sb, int* fs_stat) {
bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
- bool want_encrypt = fs_mgr_is_file_encrypted(rec) != 0;
+ bool want_encrypt = entry.fs_mgr_flags.file_encryption;
if (has_encrypt || !want_encrypt) {
return;
@@ -407,7 +430,7 @@
return;
}
- const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device};
+ const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
LINFO << "Enabling ext4 encryption on " << blk_device;
if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
@@ -417,6 +440,91 @@
}
}
+// Enable fs-verity if needed.
+static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
+ const struct ext4_super_block* sb, int* fs_stat) {
+ bool has_verity = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_VERITY)) != 0;
+ bool want_verity = entry.fs_mgr_flags.fs_verity;
+
+ if (has_verity || !want_verity) {
+ return;
+ }
+
+ std::string verity_support;
+ if (!android::base::ReadFileToString(SYSFS_EXT4_VERITY, &verity_support)) {
+ LERROR << "Failed to open " << SYSFS_EXT4_VERITY;
+ return;
+ }
+
+ if (!(android::base::Trim(verity_support) == "supported")) {
+ LERROR << "Current ext4 verity not supported by kernel";
+ return;
+ }
+
+ if (!tune2fs_available()) {
+ LERROR << "Unable to enable ext4 verity on " << blk_device
+ << " because " TUNE2FS_BIN " is missing";
+ return;
+ }
+
+ LINFO << "Enabling ext4 verity on " << blk_device;
+
+ const char* argv[] = {TUNE2FS_BIN, "-O", "verity", blk_device.c_str()};
+ if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
+ LERROR << "Failed to run " TUNE2FS_BIN " to enable "
+ << "ext4 verity on " << blk_device;
+ *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;
+ }
+}
+
+// Read the primary superblock from an f2fs filesystem. On failure return
+// false. If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
+#define F2FS_BLKSIZE 4096
+#define F2FS_SUPER_OFFSET 1024
+static bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ __le32 sb1, sb2;
+
+ if (fd < 0) {
+ PERROR << "Failed to open '" << blk_device << "'";
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {
+ PERROR << "Can't read '" << blk_device << "' superblock1";
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ sizeof(sb2)) {
+ PERROR << "Can't read '" << blk_device << "' superblock2";
+ return false;
+ }
+
+ if (sb1 != cpu_to_le32(F2FS_SUPER_MAGIC) && sb2 != cpu_to_le32(F2FS_SUPER_MAGIC)) {
+ LINFO << "Invalid f2fs superblock on '" << blk_device << "'";
+ *fs_stat |= FS_STAT_INVALID_MAGIC;
+ return false;
+ }
+ return true;
+}
+
+// exported silent version of the above that just answer the question is_f2fs
+bool fs_mgr_is_f2fs(const std::string& blk_device) {
+ android::base::ErrnoRestorer restore;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) return false;
+ __le32 sb;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {
+ return false;
+ }
+ if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
+ if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+ sizeof(sb)) {
+ return false;
+ }
+ return sb == cpu_to_le32(F2FS_SUPER_MAGIC);
+}
+
//
// Prepare the filesystem on the given block device to be mounted.
//
@@ -426,10 +534,10 @@
// If needed, we'll also enable (or disable) filesystem features as specified by
// the fstab record.
//
-static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) {
+static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry) {
int fs_stat = 0;
- if (is_extfs(rec->fs_type)) {
+ if (is_extfs(entry.fs_type)) {
struct ext4_super_block sb;
if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
@@ -442,60 +550,45 @@
}
// Note: quotas should be enabled before running fsck.
- tune_quota(blk_device, rec, &sb, &fs_stat);
+ tune_quota(blk_device, entry, &sb, &fs_stat);
} else {
return fs_stat;
}
+ } else if (is_f2fs(entry.fs_type)) {
+ if (!read_f2fs_superblock(blk_device, &fs_stat)) {
+ return fs_stat;
+ }
}
- if ((rec->fs_mgr_flags & MF_CHECK) ||
+ if (entry.fs_mgr_flags.check ||
(fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
- check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat);
+ check_fs(blk_device, entry.fs_type, entry.mount_point, &fs_stat);
}
- if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) {
+ if (is_extfs(entry.fs_type) &&
+ (entry.reserved_size != 0 || entry.fs_mgr_flags.file_encryption ||
+ entry.fs_mgr_flags.fs_verity)) {
struct ext4_super_block sb;
if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
- tune_reserved_size(blk_device, rec, &sb, &fs_stat);
- tune_encrypt(blk_device, rec, &sb, &fs_stat);
+ tune_reserved_size(blk_device, entry, &sb, &fs_stat);
+ tune_encrypt(blk_device, entry, &sb, &fs_stat);
+ tune_verity(blk_device, entry, &sb, &fs_stat);
}
}
return fs_stat;
}
-static void remove_trailing_slashes(char *n)
-{
- int len;
-
- len = strlen(n) - 1;
- while ((*(n + len) == '/') && len) {
- *(n + len) = '\0';
- len--;
- }
-}
-
-/*
- * Mark the given block device as read-only, using the BLKROSET ioctl.
- * Return 0 on success, and -1 on error.
- */
-int fs_mgr_set_blk_ro(const char *blockdev)
-{
- int fd;
- int rc = -1;
- int ON = 1;
-
- fd = TEMP_FAILURE_RETRY(open(blockdev, O_RDONLY | O_CLOEXEC));
+// Mark the given block device as read-only, using the BLKROSET ioctl.
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
- // should never happen
- return rc;
+ return false;
}
- rc = ioctl(fd, BLKROSET, &ON);
- close(fd);
-
- return rc;
+ int ON = readonly;
+ return ioctl(fd, BLKROSET, &ON) == 0;
}
// Orange state means the device is unlocked, see the following link for details.
@@ -508,40 +601,42 @@
return false;
}
-/*
- * __mount(): wrapper around the mount() system call which also
- * sets the underlying block device to read-only if the mount is read-only.
- * See "man 2 mount" for return values.
- */
-static int __mount(const char *source, const char *target, const struct fstab_rec *rec)
-{
- unsigned long mountflags = rec->flags;
- int ret;
- int save_errno;
-
- /* We need this because sometimes we have legacy symlinks
- * that are lingering around and need cleaning up.
- */
+// __mount(): wrapper around the mount() system call which also
+// sets the underlying block device to read-only if the mount is read-only.
+// See "man 2 mount" for return values.
+static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
+ // We need this because sometimes we have legacy symlinks that are
+ // lingering around and need cleaning up.
struct stat info;
- if (!lstat(target, &info))
- if ((info.st_mode & S_IFMT) == S_IFLNK)
- unlink(target);
- mkdir(target, 0755);
+ if (lstat(target.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {
+ unlink(target.c_str());
+ }
+ mkdir(target.c_str(), 0755);
errno = 0;
- ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
- save_errno = errno;
+ unsigned long mountflags = entry.flags;
+ int ret = 0;
+ int save_errno = 0;
+ do {
+ if (save_errno == EAGAIN) {
+ PINFO << "Retrying mount (source=" << source << ",target=" << target
+ << ",type=" << entry.fs_type << ")=" << ret << "(" << save_errno << ")";
+ }
+ ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+ entry.fs_options.c_str());
+ save_errno = errno;
+ } while (ret && save_errno == EAGAIN);
const char* target_missing = "";
const char* source_missing = "";
if (save_errno == ENOENT) {
- if (access(target, F_OK)) {
+ if (access(target.c_str(), F_OK)) {
target_missing = "(missing)";
- } else if (access(source, F_OK)) {
+ } else if (access(source.c_str(), F_OK)) {
source_missing = "(missing)";
}
errno = save_errno;
}
PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
- << target_missing << ",type=" << rec->fs_type << ")=" << ret;
+ << target_missing << ",type=" << entry.fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -549,249 +644,208 @@
return ret;
}
-static int fs_match(const char *in1, const char *in2)
-{
- char *n1;
- char *n2;
- int ret;
-
- n1 = strdup(in1);
- n2 = strdup(in2);
-
- remove_trailing_slashes(n1);
- remove_trailing_slashes(n2);
-
- ret = !strcmp(n1, n2);
-
- free(n1);
- free(n2);
-
- return ret;
-}
-
-/*
- * Tries to mount any of the consecutive fstab entries that match
- * the mountpoint of the one given by fstab->recs[start_idx].
- *
- * end_idx: On return, will be the last rec that was looked at.
- * attempted_idx: On return, will indicate which fstab rec
- * succeeded. In case of failure, it will be the start_idx.
- * Returns
- * -1 on failure with errno set to match the 1st mount failure.
- * 0 on success.
- */
-static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
- int i;
- int mount_errno = 0;
- int mounted = 0;
-
- if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
- errno = EINVAL;
- if (end_idx) *end_idx = start_idx;
- if (attempted_idx) *attempted_idx = start_idx;
- return -1;
+static bool fs_match(const std::string& in1, const std::string& in2) {
+ if (in1.empty() || in2.empty()) {
+ return false;
}
- /* Hunt down an fstab entry for the same mount point that might succeed */
+ auto in1_end = in1.size() - 1;
+ while (in1_end > 0 && in1[in1_end] == '/') {
+ in1_end--;
+ }
+
+ auto in2_end = in2.size() - 1;
+ while (in2_end > 0 && in2[in2_end] == '/') {
+ in2_end--;
+ }
+
+ if (in1_end != in2_end) {
+ return false;
+ }
+
+ for (size_t i = 0; i <= in1_end; ++i) {
+ if (in1[i] != in2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Tries to mount any of the consecutive fstab entries that match
+// the mountpoint of the one given by fstab[start_idx].
+//
+// end_idx: On return, will be the last entry that was looked at.
+// attempted_idx: On return, will indicate which fstab entry
+// succeeded. In case of failure, it will be the start_idx.
+// Sets errno to match the 1st mount failure on failure.
+static bool mount_with_alternatives(const Fstab& fstab, int start_idx, int* end_idx,
+ int* attempted_idx) {
+ unsigned long i;
+ int mount_errno = 0;
+ bool mounted = false;
+
+ // Hunt down an fstab entry for the same mount point that might succeed.
for (i = start_idx;
- /* We required that fstab entries for the same mountpoint be consecutive */
- i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point);
- i++) {
- /*
- * Don't try to mount/encrypt the same mount point again.
- * Deal with alternate entries for the same point which are required to be all following
- * each other.
- */
- if (mounted) {
- LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint="
- << fstab->recs[i].mount_point << " rec[" << i
- << "].fs_type=" << fstab->recs[i].fs_type
- << " already mounted as "
- << fstab->recs[*attempted_idx].fs_type;
- continue;
- }
+ // We required that fstab entries for the same mountpoint be consecutive.
+ i < fstab.size() && fstab[start_idx].mount_point == fstab[i].mount_point; i++) {
+ // Don't try to mount/encrypt the same mount point again.
+ // Deal with alternate entries for the same point which are required to be all following
+ // each other.
+ if (mounted) {
+ LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+ << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+ << fstab[*attempted_idx].fs_type;
+ continue;
+ }
- int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]);
- if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) {
- LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint="
- << fstab->recs[i].mount_point << " rec[" << i
- << "].fs_type=" << fstab->recs[i].fs_type;
- mount_errno = EINVAL; // continue bootup for FDE
- continue;
- }
+ int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
+ if (fs_stat & FS_STAT_INVALID_MAGIC) {
+ LERROR << __FUNCTION__
+ << "(): skipping mount due to invalid magic, mountpoint=" << fstab[i].mount_point
+ << " blk_dev=" << realpath(fstab[i].blk_device) << " rec[" << i
+ << "].fs_type=" << fstab[i].fs_type;
+ mount_errno = EINVAL; // continue bootup for FDE
+ continue;
+ }
- int retry_count = 2;
- while (retry_count-- > 0) {
- if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point,
- &fstab->recs[i])) {
- *attempted_idx = i;
- mounted = 1;
- if (i != start_idx) {
- LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device
- << " on " << fstab->recs[i].mount_point
- << " with fs_type=" << fstab->recs[i].fs_type << " instead of "
- << fstab->recs[start_idx].fs_type;
- }
- fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
- mount_errno = 0;
- break;
- } else {
- if (retry_count <= 0) break; // run check_fs only once
- fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
- /* back up the first errno for crypto decisions */
- if (mount_errno == 0) {
- mount_errno = errno;
- }
- // retry after fsck
- check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
- fstab->recs[i].mount_point, &fs_stat);
+ int retry_count = 2;
+ while (retry_count-- > 0) {
+ if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i])) {
+ *attempted_idx = i;
+ mounted = true;
+ if (i != start_idx) {
+ LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+ << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+ << " instead of " << fstab[start_idx].fs_type;
}
+ fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+ mount_errno = 0;
+ break;
+ } else {
+ if (retry_count <= 0) break; // run check_fs only once
+ fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
+ // back up the first errno for crypto decisions.
+ if (mount_errno == 0) {
+ mount_errno = errno;
+ }
+ // retry after fsck
+ check_fs(fstab[i].blk_device, fstab[i].fs_type, fstab[i].mount_point, &fs_stat);
}
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+ }
+ log_fs_stat(fstab[i].blk_device, fs_stat);
}
/* Adjust i for the case where it was still withing the recs[] */
- if (i < fstab->num_entries) --i;
+ if (i < fstab.size()) --i;
*end_idx = i;
if (!mounted) {
*attempted_idx = start_idx;
errno = mount_errno;
- return -1;
+ return false;
}
- return 0;
+ return true;
}
-static int translate_ext_labels(struct fstab_rec *rec)
-{
- DIR *blockdir = NULL;
- struct dirent *ent;
- char *label;
- size_t label_len;
- int ret = -1;
-
- if (strncmp(rec->blk_device, "LABEL=", 6))
- return 0;
-
- label = rec->blk_device + 6;
- label_len = strlen(label);
-
- if (label_len > 16) {
- LERROR << "FS label is longer than allowed by filesystem";
- goto out;
+static bool TranslateExtLabels(FstabEntry* entry) {
+ if (!StartsWith(entry->blk_device, "LABEL=")) {
+ return true;
}
+ std::string label = entry->blk_device.substr(6);
+ if (label.size() > 16) {
+ LERROR << "FS label is longer than allowed by filesystem";
+ return false;
+ }
- blockdir = opendir("/dev/block");
+ auto blockdir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
if (!blockdir) {
LERROR << "couldn't open /dev/block";
- goto out;
+ return false;
}
- while ((ent = readdir(blockdir))) {
- int fd;
- char super_buf[1024];
- struct ext4_super_block *sb;
-
+ struct dirent* ent;
+ while ((ent = readdir(blockdir.get()))) {
if (ent->d_type != DT_BLK)
continue;
- fd = openat(dirfd(blockdir), ent->d_name, O_RDONLY);
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ openat(dirfd(blockdir.get()), ent->d_name, O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
LERROR << "Cannot open block device /dev/block/" << ent->d_name;
- goto out;
+ return false;
}
+ ext4_super_block super_block;
if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||
- TEMP_FAILURE_RETRY(read(fd, super_buf, 1024)) != 1024) {
- /* Probably a loopback device or something else without a readable
- * superblock.
- */
- close(fd);
+ TEMP_FAILURE_RETRY(read(fd, &super_block, sizeof(super_block))) !=
+ sizeof(super_block)) {
+ // Probably a loopback device or something else without a readable superblock.
continue;
}
- sb = (struct ext4_super_block *)super_buf;
- if (sb->s_magic != EXT4_SUPER_MAGIC) {
+ if (super_block.s_magic != EXT4_SUPER_MAGIC) {
LINFO << "/dev/block/" << ent->d_name << " not ext{234}";
continue;
}
- if (!strncmp(label, sb->s_volume_name, label_len)) {
- char *new_blk_device;
+ if (label == super_block.s_volume_name) {
+ std::string new_blk_device = "/dev/block/"s + ent->d_name;
- if (asprintf(&new_blk_device, "/dev/block/%s", ent->d_name) < 0) {
- LERROR << "Could not allocate block device string";
- goto out;
- }
+ LINFO << "resolved label " << entry->blk_device << " to " << new_blk_device;
- LINFO << "resolved label " << rec->blk_device << " to "
- << new_blk_device;
-
- free(rec->blk_device);
- rec->blk_device = new_blk_device;
- ret = 0;
- break;
+ entry->blk_device = new_blk_device;
+ return true;
}
}
-out:
- closedir(blockdir);
- return ret;
+ return false;
}
-static bool needs_block_encryption(const struct fstab_rec* rec)
-{
- if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
- fs_mgr_is_encryptable(rec))
+static bool needs_block_encryption(const FstabEntry& entry) {
+ if (android::base::GetBoolProperty("ro.vold.forceencryption", false) && entry.is_encryptable())
return true;
- if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
- if (rec->fs_mgr_flags & MF_CRYPT) {
- /* Check for existence of convert_fde breadcrumb file */
- char convert_fde_name[PATH_MAX];
- snprintf(convert_fde_name, sizeof(convert_fde_name),
- "%s/misc/vold/convert_fde", rec->mount_point);
- if (access(convert_fde_name, F_OK) == 0) return true;
+ if (entry.fs_mgr_flags.force_crypt) return true;
+ if (entry.fs_mgr_flags.crypt) {
+ // Check for existence of convert_fde breadcrumb file.
+ auto convert_fde_name = entry.mount_point + "/misc/vold/convert_fde";
+ if (access(convert_fde_name.c_str(), F_OK) == 0) return true;
}
- if (rec->fs_mgr_flags & MF_FORCEFDEORFBE) {
- /* Check for absence of convert_fbe breadcrumb file */
- char convert_fbe_name[PATH_MAX];
- snprintf(convert_fbe_name, sizeof(convert_fbe_name),
- "%s/convert_fbe", rec->mount_point);
- if (access(convert_fbe_name, F_OK) != 0) return true;
+ if (entry.fs_mgr_flags.force_fde_or_fbe) {
+ // Check for absence of convert_fbe breadcrumb file.
+ auto convert_fbe_name = entry.mount_point + "/convert_fbe";
+ if (access(convert_fbe_name.c_str(), F_OK) != 0) return true;
}
return false;
}
-static bool should_use_metadata_encryption(const struct fstab_rec* rec) {
- if (!(rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE))) return false;
- if (!(rec->fs_mgr_flags & MF_KEYDIRECTORY)) return false;
- return true;
+static bool should_use_metadata_encryption(const FstabEntry& entry) {
+ return !entry.key_dir.empty() &&
+ (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
}
// Check to see if a mountable volume has encryption requirements
-static int handle_encryptable(const struct fstab_rec* rec)
-{
- /* If this is block encryptable, need to trigger encryption */
- if (needs_block_encryption(rec)) {
- if (umount(rec->mount_point) == 0) {
+static int handle_encryptable(const FstabEntry& entry) {
+ // If this is block encryptable, need to trigger encryption.
+ if (needs_block_encryption(entry)) {
+ if (umount(entry.mount_point.c_str()) == 0) {
return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
} else {
- PWARNING << "Could not umount " << rec->mount_point
- << " - allow continue unencrypted";
+ PWARNING << "Could not umount " << entry.mount_point << " - allow continue unencrypted";
return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
}
- } else if (should_use_metadata_encryption(rec)) {
- if (umount(rec->mount_point) == 0) {
+ } else if (should_use_metadata_encryption(entry)) {
+ if (umount(entry.mount_point.c_str()) == 0) {
return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
} else {
- PERROR << "Could not umount " << rec->mount_point << " - fail since can't encrypt";
+ PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
return FS_MGR_MNTALL_FAIL;
}
- } else if (rec->fs_mgr_flags & (MF_FILEENCRYPTION | MF_FORCEFDEORFBE)) {
- LINFO << rec->mount_point << " is file encrypted";
+ } else if (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe) {
+ LINFO << entry.mount_point << " is file encrypted";
return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
- } else if (fs_mgr_is_encryptable(rec)) {
+ } else if (entry.is_encryptable()) {
return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
} else {
return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -832,21 +886,21 @@
return true;
}
-bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+bool fs_mgr_update_logical_partition(FstabEntry* entry) {
// Logical partitions are specified with a named partition rather than a
// block device, so if the block device is a path, then it has already
// been updated.
- if (rec->blk_device[0] == '/') {
+ if (entry->blk_device[0] == '/') {
return true;
}
DeviceMapper& dm = DeviceMapper::Instance();
std::string device_name;
- if (!dm.GetDmDevicePathByName(rec->blk_device, &device_name)) {
+ if (!dm.GetDmDevicePathByName(entry->blk_device, &device_name)) {
return false;
}
- free(rec->blk_device);
- rec->blk_device = strdup(device_name.c_str());
+
+ entry->blk_device = device_name;
return true;
}
@@ -854,13 +908,13 @@
public:
CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
- bool Update(struct fstab_rec* rec) {
- if (!fs_mgr_is_checkpoint(rec)) {
+ bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
+ if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
return true;
}
- if (fs_mgr_is_checkpoint_blk(rec)) {
- call_vdc({"checkpoint", "restoreCheckpoint", rec->blk_device});
+ if (entry->fs_mgr_flags.checkpoint_blk) {
+ call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
}
if (needs_checkpoint_ == UNKNOWN &&
@@ -873,7 +927,7 @@
return true;
}
- if (!UpdateCheckpointPartition(rec)) {
+ if (!UpdateCheckpointPartition(entry, block_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
return false;
}
@@ -881,18 +935,17 @@
return true;
}
- bool Revert(struct fstab_rec* rec) {
- if (!fs_mgr_is_checkpoint(rec)) {
+ bool Revert(FstabEntry* entry) {
+ if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
return true;
}
- if (device_map_.find(rec->blk_device) == device_map_.end()) {
+ if (device_map_.find(entry->blk_device) == device_map_.end()) {
return true;
}
- std::string bow_device = rec->blk_device;
- free(rec->blk_device);
- rec->blk_device = strdup(device_map_[bow_device].c_str());
+ std::string bow_device = entry->blk_device;
+ entry->blk_device = device_map_[bow_device];
device_map_.erase(bow_device);
DeviceMapper& dm = DeviceMapper::Instance();
@@ -904,53 +957,51 @@
}
private:
- bool UpdateCheckpointPartition(struct fstab_rec* rec) {
- if (fs_mgr_is_checkpoint_fs(rec)) {
- if (!strcmp(rec->fs_type, "f2fs")) {
- std::string opts(rec->fs_options);
-
- opts += ",checkpoint=disable";
- free(rec->fs_options);
- rec->fs_options = strdup(opts.c_str());
+ bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {
+ if (entry->fs_mgr_flags.checkpoint_fs) {
+ if (is_f2fs(entry->fs_type)) {
+ entry->fs_options += ",checkpoint=disable";
} else {
- LERROR << rec->fs_type << " does not implement checkpoints.";
+ LERROR << entry->fs_type << " does not implement checkpoints.";
}
- } else if (fs_mgr_is_checkpoint_blk(rec)) {
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(rec->blk_device, O_RDONLY | O_CLOEXEC)));
- if (!fd) {
- PERROR << "Cannot open device " << rec->blk_device;
- return false;
- }
+ } else if (entry->fs_mgr_flags.checkpoint_blk) {
+ auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
+ if (fs_mgr_find_bow_device(actual_block_device).empty()) {
+ unique_fd fd(
+ TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Cannot open device " << entry->blk_device;
+ return false;
+ }
- uint64_t size = get_block_device_size(fd) / 512;
- if (!size) {
- PERROR << "Cannot get device size";
- return false;
- }
+ uint64_t size = get_block_device_size(fd) / 512;
+ if (!size) {
+ PERROR << "Cannot get device size";
+ return false;
+ }
- android::dm::DmTable table;
- if (!table.AddTarget(
- std::make_unique<android::dm::DmTargetBow>(0, size, rec->blk_device))) {
- LERROR << "Failed to add bow target";
- return false;
- }
+ android::dm::DmTable table;
+ if (!table.AddTarget(std::make_unique<android::dm::DmTargetBow>(
+ 0, size, entry->blk_device))) {
+ LERROR << "Failed to add bow target";
+ return false;
+ }
- DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice("bow", table)) {
- PERROR << "Failed to create bow device";
- return false;
- }
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.CreateDevice("bow", table)) {
+ PERROR << "Failed to create bow device";
+ return false;
+ }
- std::string name;
- if (!dm.GetDmDevicePathByName("bow", &name)) {
- PERROR << "Failed to get bow device name";
- return false;
- }
+ std::string name;
+ if (!dm.GetDmDevicePathByName("bow", &name)) {
+ PERROR << "Failed to get bow device name";
+ return false;
+ }
- device_map_[name] = rec->blk_device;
- free(rec->blk_device);
- rec->blk_device = strdup(name.c_str());
+ device_map_[name] = entry->blk_device;
+ entry->blk_device = name;
+ }
}
return true;
}
@@ -960,95 +1011,155 @@
std::map<std::string, std::string> device_map_;
};
-/* When multiple fstab records share the same mount_point, it will
- * try to mount each one in turn, and ignore any duplicates after a
- * first successful mount.
- * Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
- */
-int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
- int i = 0;
+std::string fs_mgr_find_bow_device(const std::string& block_device) {
+ if (block_device.substr(0, 5) != "/dev/") {
+ LOG(ERROR) << "Expected block device, got " << block_device;
+ return std::string();
+ }
+
+ std::string sys_dir = std::string("/sys/") + block_device.substr(5);
+
+ for (;;) {
+ std::string name;
+ if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
+ PLOG(ERROR) << block_device << " is not dm device";
+ return std::string();
+ }
+
+ if (name == "bow\n") return sys_dir;
+
+ std::string slaves = sys_dir + "/slaves";
+ std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(slaves.c_str()), closedir);
+ if (!directory) {
+ PLOG(ERROR) << "Can't open slave directory " << slaves;
+ return std::string();
+ }
+
+ int count = 0;
+ for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) {
+ if (entry->d_type != DT_LNK) continue;
+
+ if (count == 1) {
+ LOG(ERROR) << "Too many slaves in " << slaves;
+ return std::string();
+ }
+
+ ++count;
+ sys_dir = std::string("/sys/block/") + entry->d_name;
+ }
+
+ if (count != 1) {
+ LOG(ERROR) << "No slave in " << slaves;
+ return std::string();
+ }
+ }
+}
+
+static bool IsMountPointMounted(const std::string& mount_point) {
+ // Check if this is already mounted.
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return false;
+ }
+ return GetEntryForMountPoint(&fstab, mount_point) != nullptr;
+}
+
+// When multiple fstab records share the same mount_point, it will try to mount each
+// one in turn, and ignore any duplicates after a first successful mount.
+// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
+int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0;
- int mret = -1;
- int mount_errno = 0;
- int attempted_idx = -1;
CheckpointManager checkpoint_manager;
- FsManagerAvbUniquePtr avb_handle(nullptr);
+ AvbUniquePtr avb_handle(nullptr);
- if (!fstab) {
+ if (fstab->empty()) {
return FS_MGR_MNTALL_FAIL;
}
- for (i = 0; i < fstab->num_entries; i++) {
- /* Don't mount entries that are managed by vold or not for the mount mode*/
- if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
- ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
- ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
+ for (size_t i = 0; i < fstab->size(); i++) {
+ auto& current_entry = (*fstab)[i];
+
+ // If a filesystem should have been mounted in the first stage, we
+ // ignore it here. With one exception, if the filesystem is
+ // formattable, then it can only be formatted in the second stage,
+ // so we allow it to mount here.
+ if (current_entry.fs_mgr_flags.first_stage_mount &&
+ (!current_entry.fs_mgr_flags.formattable ||
+ IsMountPointMounted(current_entry.mount_point))) {
continue;
}
- /* Skip swap and raw partition entries such as boot, recovery, etc */
- if (!strcmp(fstab->recs[i].fs_type, "swap") ||
- !strcmp(fstab->recs[i].fs_type, "emmc") ||
- !strcmp(fstab->recs[i].fs_type, "mtd")) {
+ // Don't mount entries that are managed by vold or not for the mount mode.
+ if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||
+ ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||
+ ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {
continue;
}
- /* Skip mounting the root partition, as it will already have been mounted */
- if (!strcmp(fstab->recs[i].mount_point, "/") ||
- !strcmp(fstab->recs[i].mount_point, "/system")) {
- if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
- fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
+ // Skip swap and raw partition entries such as boot, recovery, etc.
+ if (current_entry.fs_type == "swap" || current_entry.fs_type == "emmc" ||
+ current_entry.fs_type == "mtd") {
+ continue;
+ }
+
+ // Skip mounting the root partition, as it will already have been mounted.
+ if (current_entry.mount_point == "/" || current_entry.mount_point == "/system") {
+ if ((current_entry.flags & MS_RDONLY) != 0) {
+ fs_mgr_set_blk_ro(current_entry.blk_device);
}
continue;
}
- /* Translate LABEL= file system labels into block devices */
- if (is_extfs(fstab->recs[i].fs_type)) {
- int tret = translate_ext_labels(&fstab->recs[i]);
- if (tret < 0) {
+ // Translate LABEL= file system labels into block devices.
+ if (is_extfs(current_entry.fs_type)) {
+ if (!TranslateExtLabels(¤t_entry)) {
LERROR << "Could not translate label to block device";
continue;
}
}
- if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
- if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+ if (current_entry.fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(¤t_entry)) {
LERROR << "Could not set up logical partition, skipping!";
continue;
}
}
- if (!checkpoint_manager.Update(&fstab->recs[i])) {
+ if (!checkpoint_manager.Update(¤t_entry)) {
continue;
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
- !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
- LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+ if (current_entry.fs_mgr_flags.wait && !WaitForFile(current_entry.blk_device, 20s)) {
+ LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
continue;
}
- if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+ if (current_entry.fs_mgr_flags.avb) {
if (!avb_handle) {
- avb_handle = FsManagerAvbHandle::Open(*fstab);
+ avb_handle = AvbHandle::Open();
if (!avb_handle) {
- LERROR << "Failed to open FsManagerAvbHandle";
+ LERROR << "Failed to open AvbHandle";
return FS_MGR_MNTALL_FAIL;
}
}
- if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
- SetUpAvbHashtreeResult::kFail) {
- LERROR << "Failed to set up AVB on partition: "
- << fstab->recs[i].mount_point << ", skipping!";
- /* Skips mounting the device. */
+ if (avb_handle->SetUpAvbHashtree(¤t_entry, true /* wait_for_verity_dev */) ==
+ AvbHashtreeResult::kFail) {
+ LERROR << "Failed to set up AVB on partition: " << current_entry.mount_point
+ << ", skipping!";
+ // Skips mounting the device.
continue;
}
- } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() &&
- (rc == FS_MGR_SETUP_VERITY_DISABLED ||
- rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+ } else if (!current_entry.avb_keys.empty()) {
+ if (AvbHandle::SetUpStandaloneAvbHashtree(¤t_entry) == AvbHashtreeResult::kFail) {
+ LERROR << "Failed to set up AVB on standalone partition: "
+ << current_entry.mount_point << ", skipping!";
+ // Skips mounting the device.
+ continue;
+ }
+ } else if ((current_entry.fs_mgr_flags.verify)) {
+ int rc = fs_mgr_setup_verity(¤t_entry, true);
+ if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1058,17 +1169,19 @@
int last_idx_inspected;
int top_idx = i;
+ int attempted_idx = -1;
- mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);
+ bool mret = mount_with_alternatives(*fstab, i, &last_idx_inspected, &attempted_idx);
+ auto& attempted_entry = (*fstab)[attempted_idx];
i = last_idx_inspected;
- mount_errno = errno;
+ int mount_errno = errno;
- /* Deal with encryptability. */
- if (!mret) {
- int status = handle_encryptable(&fstab->recs[attempted_idx]);
+ // Handle success and deal with encryptability.
+ if (mret) {
+ int status = handle_encryptable(attempted_entry);
if (status == FS_MGR_MNTALL_FAIL) {
- /* Fatal error - no point continuing */
+ // Fatal error - no point continuing.
return status;
}
@@ -1079,51 +1192,46 @@
}
encryptable = status;
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
- if (!call_vdc(
- {"cryptfs", "encryptFstab", fstab->recs[attempted_idx].mount_point})) {
+ if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
+ attempted_entry.mount_point})) {
LERROR << "Encryption failed";
return FS_MGR_MNTALL_FAIL;
}
}
}
- /* Success! Go get the next one */
+ // Success! Go get the next one.
continue;
}
- bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+ // Mounting failed, understand why and retry.
+ bool wiped = partition_wiped(current_entry.blk_device.c_str());
bool crypt_footer = false;
- if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
- fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
- /* top_idx and attempted_idx point at the same partition, but sometimes
- * at two different lines in the fstab. Use the top one for formatting
- * as that is the preferred one.
- */
- LERROR << __FUNCTION__ << "(): " << fstab->recs[top_idx].blk_device
- << " is wiped and " << fstab->recs[top_idx].mount_point
- << " " << fstab->recs[top_idx].fs_type
+ if (mount_errno != EBUSY && mount_errno != EACCES &&
+ current_entry.fs_mgr_flags.formattable && wiped) {
+ // current_entry and attempted_entry point at the same partition, but sometimes
+ // at two different lines in the fstab. Use current_entry for formatting
+ // as that is the preferred one.
+ LERROR << __FUNCTION__ << "(): " << realpath(current_entry.blk_device)
+ << " is wiped and " << current_entry.mount_point << " " << current_entry.fs_type
<< " is formattable. Format it.";
- checkpoint_manager.Revert(&fstab->recs[top_idx]);
+ checkpoint_manager.Revert(¤t_entry);
- if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
- strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
- int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
+ if (current_entry.is_encryptable() && current_entry.key_loc != KEY_IN_FOOTER) {
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(current_entry.key_loc.c_str(), O_WRONLY | O_CLOEXEC)));
if (fd >= 0) {
- LINFO << __FUNCTION__ << "(): also wipe "
- << fstab->recs[top_idx].key_loc;
+ LINFO << __FUNCTION__ << "(): also wipe " << current_entry.key_loc;
wipe_block_device(fd, get_file_size(fd));
- close(fd);
} else {
- PERROR << __FUNCTION__ << "(): "
- << fstab->recs[top_idx].key_loc << " wouldn't open";
+ PERROR << __FUNCTION__ << "(): " << current_entry.key_loc << " wouldn't open";
}
- } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
- !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+ } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
crypt_footer = true;
}
- if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
- /* Let's replay the mount actions. */
+ if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
+ // Let's replay the mount actions.
i = top_idx - 1;
continue;
} else {
@@ -1134,35 +1242,30 @@
}
}
- /* mount(2) returned an error, handle the encryptable/formattable case */
- if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
- fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
+ // mount(2) returned an error, handle the encryptable/formattable case.
+ if (mount_errno != EBUSY && mount_errno != EACCES && attempted_entry.is_encryptable()) {
if (wiped) {
- LERROR << __FUNCTION__ << "(): "
- << fstab->recs[attempted_idx].blk_device
- << " is wiped and "
- << fstab->recs[attempted_idx].mount_point << " "
- << fstab->recs[attempted_idx].fs_type
+ LERROR << __FUNCTION__ << "(): " << attempted_entry.blk_device << " is wiped and "
+ << attempted_entry.mount_point << " " << attempted_entry.fs_type
<< " is encryptable. Suggest recovery...";
encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
continue;
} else {
- /* Need to mount a tmpfs at this mountpoint for now, and set
- * properties that vold will query later for decrypting
- */
+ // Need to mount a tmpfs at this mountpoint for now, and set
+ // properties that vold will query later for decrypting
LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
- << fstab->recs[attempted_idx].blk_device
- << " for mount " << fstab->recs[attempted_idx].mount_point
- << " type " << fstab->recs[attempted_idx].fs_type;
- if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {
+ << attempted_entry.blk_device << " for mount " << attempted_entry.mount_point
+ << " type " << attempted_entry.fs_type;
+ if (fs_mgr_do_tmpfs_mount(attempted_entry.mount_point.c_str()) < 0) {
++error_count;
continue;
}
}
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
- } else if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
- should_use_metadata_encryption(&fstab->recs[attempted_idx])) {
- if (!call_vdc({"cryptfs", "mountFstab", fstab->recs[attempted_idx].mount_point})) {
+ } else if (mount_errno != EBUSY && mount_errno != EACCES &&
+ should_use_metadata_encryption(attempted_entry)) {
+ if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
+ attempted_entry.mount_point})) {
++error_count;
}
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1170,18 +1273,18 @@
} else {
// fs_options might be null so we cannot use PERROR << directly.
// Use StringPrintf to output "(null)" instead.
- if (fs_mgr_is_nofail(&fstab->recs[attempted_idx])) {
+ if (attempted_entry.fs_mgr_flags.no_fail) {
PERROR << android::base::StringPrintf(
- "Ignoring failure to mount an un-encryptable or wiped "
- "partition on %s at %s options: %s",
- fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_options);
+ "Ignoring failure to mount an un-encryptable or wiped "
+ "partition on %s at %s options: %s",
+ attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+ attempted_entry.fs_options.c_str());
} else {
PERROR << android::base::StringPrintf(
- "Failed to mount an un-encryptable or wiped partition "
- "on %s at %s options: %s",
- fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
- fstab->recs[attempted_idx].fs_options);
+ "Failed to mount an un-encryptable or wiped partition "
+ "on %s at %s options: %s",
+ attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),
+ attempted_entry.fs_options.c_str());
++error_count;
}
continue;
@@ -1199,20 +1302,54 @@
}
}
-/* wrapper to __mount() and expects a fully prepared fstab_rec,
- * unlike fs_mgr_do_mount which does more things with avb / verity
- * etc.
- */
-int fs_mgr_do_mount_one(struct fstab_rec *rec)
-{
- if (!rec) {
- return FS_MGR_DOMNT_FAILED;
+int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {
+ AvbUniquePtr avb_handle(nullptr);
+ int ret = FsMgrUmountStatus::SUCCESS;
+ for (auto& current_entry : *fstab) {
+ if (!IsMountPointMounted(current_entry.mount_point)) {
+ continue;
+ }
+
+ if (umount(current_entry.mount_point.c_str()) == -1) {
+ PERROR << "Failed to umount " << current_entry.mount_point;
+ ret |= FsMgrUmountStatus::ERROR_UMOUNT;
+ continue;
+ }
+
+ if (current_entry.fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(¤t_entry)) {
+ LERROR << "Could not get logical partition blk_device, skipping!";
+ ret |= FsMgrUmountStatus::ERROR_DEVICE_MAPPER;
+ continue;
+ }
+ }
+
+ if (current_entry.fs_mgr_flags.avb || !current_entry.avb_keys.empty()) {
+ if (!AvbHandle::TearDownAvbHashtree(¤t_entry, true /* wait */)) {
+ LERROR << "Failed to tear down AVB on mount point: " << current_entry.mount_point;
+ ret |= FsMgrUmountStatus::ERROR_VERITY;
+ continue;
+ }
+ } else if ((current_entry.fs_mgr_flags.verify)) {
+ if (!fs_mgr_teardown_verity(¤t_entry)) {
+ LERROR << "Failed to tear down verified partition on mount point: "
+ << current_entry.mount_point;
+ ret |= FsMgrUmountStatus::ERROR_VERITY;
+ continue;
+ }
+ }
}
+ return ret;
+}
+// wrapper to __mount() and expects a fully prepared fstab_rec,
+// unlike fs_mgr_do_mount which does more things with avb / verity etc.
+int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& mount_point) {
// Run fsck if needed
- prepare_fs_for_mount(rec->blk_device, rec);
+ prepare_fs_for_mount(entry.blk_device, entry);
- int ret = __mount(rec->blk_device, rec->mount_point, rec);
+ int ret =
+ __mount(entry.blk_device, mount_point.empty() ? entry.mount_point : mount_point, entry);
if (ret) {
ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
}
@@ -1220,79 +1357,82 @@
return ret;
}
-/* If tmp_mount_point is non-null, mount the filesystem there. This is for the
- * tmp mount we do to check the user password
- * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
- * in turn, and stop on 1st success, or no more match.
- */
-static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
- char* tmp_mount_point, int needs_checkpoint) {
- int i = 0;
+// If tmp_mount_point is non-null, mount the filesystem there. This is for the
+// tmp mount we do to check the user password
+// If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
+// in turn, and stop on 1st success, or no more match.
+static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
+ const std::string& n_blk_device, const char* tmp_mount_point,
+ int needs_checkpoint) {
int mount_errors = 0;
int first_mount_errno = 0;
- char* mount_point;
+ std::string mount_point;
CheckpointManager checkpoint_manager(needs_checkpoint);
- FsManagerAvbUniquePtr avb_handle(nullptr);
+ AvbUniquePtr avb_handle(nullptr);
if (!fstab) {
return FS_MGR_DOMNT_FAILED;
}
- for (i = 0; i < fstab->num_entries; i++) {
- if (!fs_match(fstab->recs[i].mount_point, n_name)) {
+ for (auto& fstab_entry : *fstab) {
+ if (!fs_match(fstab_entry.mount_point, n_name)) {
continue;
}
- /* We found our match */
- /* If this swap or a raw partition, report an error */
- if (!strcmp(fstab->recs[i].fs_type, "swap") ||
- !strcmp(fstab->recs[i].fs_type, "emmc") ||
- !strcmp(fstab->recs[i].fs_type, "mtd")) {
- LERROR << "Cannot mount filesystem of type "
- << fstab->recs[i].fs_type << " on " << n_blk_device;
+ // We found our match.
+ // If this swap or a raw partition, report an error.
+ if (fstab_entry.fs_type == "swap" || fstab_entry.fs_type == "emmc" ||
+ fstab_entry.fs_type == "mtd") {
+ LERROR << "Cannot mount filesystem of type " << fstab_entry.fs_type << " on "
+ << n_blk_device;
return FS_MGR_DOMNT_FAILED;
}
- if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
- if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+ if (fstab_entry.fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(&fstab_entry)) {
LERROR << "Could not set up logical partition, skipping!";
continue;
}
}
- if (!checkpoint_manager.Update(&fstab->recs[i])) {
+ if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
continue;
}
- /* First check the filesystem if requested */
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+ // First check the filesystem if requested.
+ if (fstab_entry.fs_mgr_flags.wait && !WaitForFile(n_blk_device, 20s)) {
LERROR << "Skipping mounting '" << n_blk_device << "'";
continue;
}
- int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
+ int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry);
- if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
+ if (fstab_entry.fs_mgr_flags.avb) {
if (!avb_handle) {
- avb_handle = FsManagerAvbHandle::Open(*fstab);
+ avb_handle = AvbHandle::Open();
if (!avb_handle) {
- LERROR << "Failed to open FsManagerAvbHandle";
+ LERROR << "Failed to open AvbHandle";
return FS_MGR_DOMNT_FAILED;
}
}
- if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) ==
- SetUpAvbHashtreeResult::kFail) {
- LERROR << "Failed to set up AVB on partition: "
- << fstab->recs[i].mount_point << ", skipping!";
- /* Skips mounting the device. */
+ if (avb_handle->SetUpAvbHashtree(&fstab_entry, true /* wait_for_verity_dev */) ==
+ AvbHashtreeResult::kFail) {
+ LERROR << "Failed to set up AVB on partition: " << fstab_entry.mount_point
+ << ", skipping!";
+ // Skips mounting the device.
continue;
}
- } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
- int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
- if (__android_log_is_debuggable() &&
- (rc == FS_MGR_SETUP_VERITY_DISABLED ||
- rc == FS_MGR_SETUP_VERITY_SKIPPED)) {
+ } else if (!fstab_entry.avb_keys.empty()) {
+ if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {
+ LERROR << "Failed to set up AVB on standalone partition: "
+ << fstab_entry.mount_point << ", skipping!";
+ // Skips mounting the device.
+ continue;
+ }
+ } else if (fstab_entry.fs_mgr_flags.verify) {
+ int rc = fs_mgr_setup_verity(&fstab_entry, true);
+ if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
LINFO << "Verity disabled";
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
LERROR << "Could not set up verified partition, skipping!";
@@ -1300,15 +1440,15 @@
}
}
- /* Now mount it where requested */
+ // Now mount it where requested */
if (tmp_mount_point) {
mount_point = tmp_mount_point;
} else {
- mount_point = fstab->recs[i].mount_point;
+ mount_point = fstab_entry.mount_point;
}
int retry_count = 2;
while (retry_count-- > 0) {
- if (!__mount(n_blk_device, mount_point, &fstab->recs[i])) {
+ if (!__mount(n_blk_device, mount_point, fstab_entry)) {
fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
return FS_MGR_DOMNT_SUCCESS;
} else {
@@ -1317,10 +1457,10 @@
mount_errors++;
fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
// try again after fsck
- check_fs(n_blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat);
+ check_fs(n_blk_device, fstab_entry.fs_type, fstab_entry.mount_point, &fs_stat);
}
}
- log_fs_stat(fstab->recs[i].blk_device, fs_stat);
+ log_fs_stat(fstab_entry.blk_device, fs_stat);
}
// Reach here means the mount attempt fails.
@@ -1328,17 +1468,17 @@
PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point;
if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;
} else {
- /* We didn't find a match, say so and return an error */
+ // We didn't find a match, say so and return an error.
LERROR << "Cannot find mount point " << n_name << " in fstab";
}
return FS_MGR_DOMNT_FAILED;
}
-int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
}
-int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
bool needs_checkpoint) {
return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint);
}
@@ -1362,232 +1502,216 @@
return 0;
}
-/* This must be called after mount_all, because the mkswap command needs to be
- * available.
- */
-int fs_mgr_swapon_all(fstab* fstab) {
- int i = 0;
- int flags = 0;
- int err = 0;
- int ret = 0;
- int status;
- const char *mkswap_argv[2] = {
- MKSWAP_BIN,
- nullptr
- };
+static bool InstallZramDevice(const std::string& device) {
+ if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {
+ PERROR << "Cannot write " << device << " in: " << ZRAM_BACK_DEV;
+ return false;
+ }
+ LINFO << "Success to set " << device << " to " << ZRAM_BACK_DEV;
+ return true;
+}
- if (!fstab) {
- return -1;
+static bool PrepareZramDevice(const std::string& loop, off64_t size, const std::string& bdev) {
+ if (loop.empty() && bdev.empty()) return true;
+
+ if (bdev.length()) {
+ return InstallZramDevice(bdev);
}
- for (i = 0; i < fstab->num_entries; i++) {
- /* Skip non-swap entries */
- if (strcmp(fstab->recs[i].fs_type, "swap")) {
+ // Get free loopback
+ unique_fd loop_fd(TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC)));
+ if (loop_fd.get() == -1) {
+ PERROR << "Cannot open loop-control";
+ return false;
+ }
+
+ int num = ioctl(loop_fd.get(), LOOP_CTL_GET_FREE);
+ if (num == -1) {
+ PERROR << "Cannot get free loop slot";
+ return false;
+ }
+
+ // Prepare target path
+ unique_fd target_fd(TEMP_FAILURE_RETRY(open(loop.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)));
+ if (target_fd.get() == -1) {
+ PERROR << "Cannot open target path: " << loop;
+ return false;
+ }
+ if (fallocate(target_fd.get(), 0, 0, size) < 0) {
+ PERROR << "Cannot truncate target path: " << loop;
+ return false;
+ }
+
+ // Connect loopback (device_fd) to target path (target_fd)
+ std::string device = android::base::StringPrintf("/dev/block/loop%d", num);
+ unique_fd device_fd(TEMP_FAILURE_RETRY(open(device.c_str(), O_RDWR | O_CLOEXEC)));
+ if (device_fd.get() == -1) {
+ PERROR << "Cannot open /dev/block/loop" << num;
+ return false;
+ }
+
+ if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get())) {
+ PERROR << "Cannot set loopback to target path";
+ return false;
+ }
+
+ // set block size & direct IO
+ if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096)) {
+ PWARNING << "Cannot set 4KB blocksize to /dev/block/loop" << num;
+ }
+ if (ioctl(device_fd.get(), LOOP_SET_DIRECT_IO, 1)) {
+ PWARNING << "Cannot set direct_io to /dev/block/loop" << num;
+ }
+
+ return InstallZramDevice(device);
+}
+
+bool fs_mgr_swapon_all(const Fstab& fstab) {
+ bool ret = true;
+ for (const auto& entry : fstab) {
+ // Skip non-swap entries.
+ if (entry.fs_type != "swap") {
continue;
}
- if (fstab->recs[i].zram_size > 0) {
- /* A zram_size was specified, so we need to configure the
- * device. There is no point in having multiple zram devices
- * on a system (all the memory comes from the same pool) so
- * we can assume the device number is 0.
- */
- if (fstab->recs[i].max_comp_streams >= 0) {
+ if (!PrepareZramDevice(entry.zram_loopback_path, entry.zram_loopback_size, entry.zram_backing_dev_path)) {
+ LERROR << "Skipping losetup for '" << entry.blk_device << "'";
+ }
+
+ if (entry.zram_size > 0) {
+ // A zram_size was specified, so we need to configure the
+ // device. There is no point in having multiple zram devices
+ // on a system (all the memory comes from the same pool) so
+ // we can assume the device number is 0.
+ if (entry.max_comp_streams >= 0) {
auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{
fopen(ZRAM_CONF_MCS, "re"), fclose};
- if (zram_mcs_fp == NULL) {
+ if (zram_mcs_fp == nullptr) {
LERROR << "Unable to open zram conf comp device " << ZRAM_CONF_MCS;
- ret = -1;
+ ret = false;
continue;
}
- fprintf(zram_mcs_fp.get(), "%d\n", fstab->recs[i].max_comp_streams);
+ fprintf(zram_mcs_fp.get(), "%d\n", entry.max_comp_streams);
}
auto zram_fp =
std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, "re+"), fclose};
- if (zram_fp == NULL) {
+ if (zram_fp == nullptr) {
LERROR << "Unable to open zram conf device " << ZRAM_CONF_DEV;
- ret = -1;
+ ret = false;
continue;
}
- fprintf(zram_fp.get(), "%u\n", fstab->recs[i].zram_size);
+ fprintf(zram_fp.get(), "%" PRId64 "\n", entry.zram_size);
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
- !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
- LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
- ret = -1;
+ if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
+ LERROR << "Skipping mkswap for '" << entry.blk_device << "'";
+ ret = false;
continue;
}
- /* Initialize the swap area */
- mkswap_argv[1] = fstab->recs[i].blk_device;
- err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv),
- const_cast<char **>(mkswap_argv),
- &status, true, LOG_KLOG, false, NULL,
- NULL, 0);
+ // Initialize the swap area.
+ const char* mkswap_argv[2] = {
+ MKSWAP_BIN,
+ entry.blk_device.c_str(),
+ };
+ int err = 0;
+ int status;
+ err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), const_cast<char**>(mkswap_argv),
+ &status, true, LOG_KLOG, false, nullptr, nullptr, 0);
if (err) {
- LERROR << "mkswap failed for " << fstab->recs[i].blk_device;
- ret = -1;
+ LERROR << "mkswap failed for " << entry.blk_device;
+ ret = false;
continue;
}
/* If -1, then no priority was specified in fstab, so don't set
* SWAP_FLAG_PREFER or encode the priority */
- if (fstab->recs[i].swap_prio >= 0) {
- flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) &
- SWAP_FLAG_PRIO_MASK;
+ int flags = 0;
+ if (entry.swap_prio >= 0) {
+ flags = (entry.swap_prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;
flags |= SWAP_FLAG_PREFER;
} else {
flags = 0;
}
- err = swapon(fstab->recs[i].blk_device, flags);
+ err = swapon(entry.blk_device.c_str(), flags);
if (err) {
- LERROR << "swapon failed for " << fstab->recs[i].blk_device;
- ret = -1;
+ LERROR << "swapon failed for " << entry.blk_device;
+ ret = false;
}
}
return ret;
}
-struct fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab) {
- int i;
-
- if (!fstab) {
- return NULL;
- }
-
- /* Look for the encryptable partition to find the data */
- for (i = 0; i < fstab->num_entries; i++) {
- /* Don't deal with vold managed enryptable partitions here */
- if (!(fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) &&
- (fstab->recs[i].fs_mgr_flags &
- (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE | MF_FILEENCRYPTION))) {
- return &fstab->recs[i];
- }
- }
- return NULL;
-}
-
-/*
- * key_loc must be at least PROPERTY_VALUE_MAX bytes long
- *
- * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
- */
-void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
- struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
- if (key_loc) {
- if (rec) {
- strlcpy(key_loc, rec->key_loc, size);
- } else {
- *key_loc = '\0';
- }
- }
- if (real_blk_device) {
- if (rec) {
- strlcpy(real_blk_device, rec->blk_device, size);
- } else {
- *real_blk_device = '\0';
- }
- }
-}
-
-bool fs_mgr_load_verity_state(int* mode) {
- /* return the default mode, unless any of the verified partitions are in
- * logging mode, in which case return that */
- *mode = VERITY_MODE_DEFAULT;
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (!fstab) {
- LERROR << "Failed to read default fstab";
- return false;
- }
-
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fs_mgr_is_avb(&fstab->recs[i])) {
- *mode = VERITY_MODE_RESTART; // avb only supports restart mode.
- break;
- } else if (!fs_mgr_is_verified(&fstab->recs[i])) {
- continue;
- }
-
- int current;
- if (load_verity_state(&fstab->recs[i], ¤t) < 0) {
- continue;
- }
- if (current != VERITY_MODE_DEFAULT) {
- *mode = current;
- break;
- }
- }
-
- return true;
-}
-
-bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback) {
- if (!callback) {
- return false;
- }
-
- int mode;
- if (!fs_mgr_load_verity_state(&mode)) {
- return false;
- }
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (!fstab) {
- LERROR << "Failed to read default fstab";
+bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
+ if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
}
DeviceMapper& dm = DeviceMapper::Instance();
- for (int i = 0; i < fstab->num_entries; i++) {
- auto fsrec = &fstab->recs[i];
- if (!fs_mgr_is_verified(fsrec) && !fs_mgr_is_avb(fsrec)) {
- continue;
- }
-
- std::string mount_point;
- if (!strcmp(fsrec->mount_point, "/")) {
- // In AVB, the dm device name is vroot instead of system.
- mount_point = fs_mgr_is_avb(fsrec) ? "vroot" : "system";
- } else {
- mount_point = basename(fsrec->mount_point);
- }
-
- if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
- PERROR << "Could not find verity device for mount point: " << mount_point;
- continue;
- }
-
- const char* status;
- std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
- if (!fs_mgr_is_verifyatboot(fsrec)) {
- PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point;
- continue;
- }
- status = "V";
- } else {
- status = table[0].data.c_str();
- }
-
- // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
- // back to 'system' for the callback. So it has property [partition.system.verified]
- // instead of [partition.vroot.verified].
- if (mount_point == "vroot") mount_point = "system";
- if (*status == 'C' || *status == 'V') {
- callback(fsrec, mount_point.c_str(), mode, *status);
- }
+ std::string mount_point = GetVerityDeviceName(entry);
+ if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
+ return false;
}
- return true;
+ const char* status;
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
+ if (!entry.fs_mgr_flags.verify_at_boot) {
+ return false;
+ }
+ status = "V";
+ } else {
+ status = table[0].data.c_str();
+ }
+
+ if (*status == 'C' || *status == 'V') {
+ return true;
+ }
+
+ return false;
}
-std::string fs_mgr_get_super_partition_name(int /* slot */) {
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
+ if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+ return false;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string device = GetVerityDeviceName(entry);
+
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
+ return false;
+ }
+ for (const auto& target : table) {
+ if (strcmp(target.spec.target_type, "verity") == 0 &&
+ target.data.find("check_at_most_once") != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string fs_mgr_get_super_partition_name(int slot) {
+ // Devices upgrading to dynamic partitions are allowed to specify a super
+ // partition name. This includes cuttlefish, which is a non-A/B device.
+ std::string super_partition;
+ if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ if (fs_mgr_get_slot_suffix().empty()) {
+ return super_partition;
+ }
+ std::string suffix;
+ if (slot == 0) {
+ suffix = "_a";
+ } else if (slot == 1) {
+ suffix = "_b";
+ } else if (slot == -1) {
+ suffix = fs_mgr_get_slot_suffix();
+ }
+ return super_partition + suffix;
+ }
return LP_METADATA_DEFAULT_PARTITION_NAME;
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
deleted file mode 100644
index 7c6093e..0000000
--- a/fs_mgr/fs_mgr_avb.cpp
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fs_mgr_avb.h"
-
-#include <fcntl.h>
-#include <libgen.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libavb/libavb.h>
-#include <libdm/dm.h>
-
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_avb_ops.h"
-#include "fs_mgr_priv_sha.h"
-
-static inline bool nibble_value(const char& c, uint8_t* value) {
- FS_MGR_CHECK(value != nullptr);
-
- switch (c) {
- case '0' ... '9':
- *value = c - '0';
- break;
- case 'a' ... 'f':
- *value = c - 'a' + 10;
- break;
- case 'A' ... 'F':
- *value = c - 'A' + 10;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-static bool hex_to_bytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
- FS_MGR_CHECK(bytes != nullptr);
-
- if (hex.size() % 2 != 0) {
- return false;
- }
- if (hex.size() / 2 > bytes_len) {
- return false;
- }
- for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
- uint8_t high;
- if (!nibble_value(hex[i], &high)) {
- return false;
- }
- uint8_t low;
- if (!nibble_value(hex[i + 1], &low)) {
- return false;
- }
- bytes[j] = (high << 4) | low;
- }
- return true;
-}
-
-static std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
- FS_MGR_CHECK(bytes != nullptr);
-
- static const char* hex_digits = "0123456789abcdef";
- std::string hex;
-
- for (size_t i = 0; i < bytes_len; i++) {
- hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
- hex.push_back(hex_digits[bytes[i] & 0x0F]);
- }
- return hex;
-}
-
-template <typename Hasher>
-static std::pair<size_t, bool> verify_vbmeta_digest(const AvbSlotVerifyData& verify_data,
- const uint8_t* expected_digest) {
- size_t total_size = 0;
- Hasher hasher;
- for (size_t n = 0; n < verify_data.num_vbmeta_images; n++) {
- hasher.update(verify_data.vbmeta_images[n].vbmeta_data,
- verify_data.vbmeta_images[n].vbmeta_size);
- total_size += verify_data.vbmeta_images[n].vbmeta_size;
- }
-
- bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
-
- return std::make_pair(total_size, matched);
-}
-
-// Reads the following values from kernel cmdline and provides the
-// VerifyVbmetaImages() to verify AvbSlotVerifyData.
-// - androidboot.vbmeta.hash_alg
-// - androidboot.vbmeta.size
-// - androidboot.vbmeta.digest
-class FsManagerAvbVerifier {
- public:
- // The factory method to return a unique_ptr<FsManagerAvbVerifier>
- static std::unique_ptr<FsManagerAvbVerifier> Create();
- bool VerifyVbmetaImages(const AvbSlotVerifyData& verify_data);
-
- protected:
- FsManagerAvbVerifier() = default;
-
- private:
- enum HashAlgorithm {
- kInvalid = 0,
- kSHA256 = 1,
- kSHA512 = 2,
- };
-
- HashAlgorithm hash_alg_;
- uint8_t digest_[SHA512_DIGEST_LENGTH];
- size_t vbmeta_size_;
-};
-
-std::unique_ptr<FsManagerAvbVerifier> FsManagerAvbVerifier::Create() {
- std::unique_ptr<FsManagerAvbVerifier> avb_verifier(new FsManagerAvbVerifier());
- if (!avb_verifier) {
- LERROR << "Failed to create unique_ptr<FsManagerAvbVerifier>";
- return nullptr;
- }
-
- std::string value;
- if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
- !android::base::ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
- LERROR << "Invalid hash size: " << value.c_str();
- return nullptr;
- }
-
- // Reads hash algorithm.
- size_t expected_digest_size = 0;
- std::string hash_alg;
- fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
- if (hash_alg == "sha256") {
- expected_digest_size = SHA256_DIGEST_LENGTH * 2;
- avb_verifier->hash_alg_ = kSHA256;
- } else if (hash_alg == "sha512") {
- expected_digest_size = SHA512_DIGEST_LENGTH * 2;
- avb_verifier->hash_alg_ = kSHA512;
- } else {
- LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
- return nullptr;
- }
-
- // Reads digest.
- std::string digest;
- fs_mgr_get_boot_config("vbmeta.digest", &digest);
- if (digest.size() != expected_digest_size) {
- LERROR << "Unexpected digest size: " << digest.size()
- << " (expected: " << expected_digest_size << ")";
- return nullptr;
- }
-
- if (!hex_to_bytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
- LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
- return nullptr;
- }
-
- return avb_verifier;
-}
-
-bool FsManagerAvbVerifier::VerifyVbmetaImages(const AvbSlotVerifyData& verify_data) {
- if (verify_data.num_vbmeta_images == 0) {
- LERROR << "No vbmeta images";
- return false;
- }
-
- size_t total_size = 0;
- bool digest_matched = false;
-
- if (hash_alg_ == kSHA256) {
- std::tie(total_size, digest_matched) =
- verify_vbmeta_digest<SHA256Hasher>(verify_data, digest_);
- } else if (hash_alg_ == kSHA512) {
- std::tie(total_size, digest_matched) =
- verify_vbmeta_digest<SHA512Hasher>(verify_data, digest_);
- }
-
- if (total_size != vbmeta_size_) {
- LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
- << ")";
- return false;
- }
-
- if (!digest_matched) {
- LERROR << "vbmeta digest mismatch";
- return false;
- }
-
- return true;
-}
-
-// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
-// See the following link for more details:
-// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
-static bool construct_verity_table(const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest,
- const std::string& blk_device, android::dm::DmTable* table) {
- // Loads androidboot.veritymode from kernel cmdline.
- std::string verity_mode;
- if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
- verity_mode = "enforcing"; // Defaults to enforcing when it's absent.
- }
-
- // Converts veritymode to the format used in kernel.
- std::string dm_verity_mode;
- if (verity_mode == "enforcing") {
- dm_verity_mode = "restart_on_corruption";
- } else if (verity_mode == "logging") {
- dm_verity_mode = "ignore_corruption";
- } else if (verity_mode != "eio") { // Default dm_verity_mode is eio.
- LERROR << "Unknown androidboot.veritymode: " << verity_mode;
- return false;
- }
-
- std::ostringstream hash_algorithm;
- hash_algorithm << hashtree_desc.hash_algorithm;
-
- android::dm::DmTargetVerity target(0, hashtree_desc.image_size / 512,
- hashtree_desc.dm_verity_version, blk_device, blk_device,
- hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
- hashtree_desc.image_size / hashtree_desc.data_block_size,
- hashtree_desc.tree_offset / hashtree_desc.hash_block_size,
- hash_algorithm.str(), root_digest, salt);
- if (hashtree_desc.fec_size > 0) {
- target.UseFec(blk_device, hashtree_desc.fec_num_roots,
- hashtree_desc.fec_offset / hashtree_desc.data_block_size,
- hashtree_desc.fec_offset / hashtree_desc.data_block_size);
- }
- if (!dm_verity_mode.empty()) {
- target.SetVerityMode(dm_verity_mode);
- }
- // Always use ignore_zero_blocks.
- target.IgnoreZeroBlocks();
-
- LINFO << "Built verity table: '" << target.GetParameterString() << "'";
-
- return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
-}
-
-static bool hashtree_dm_verity_setup(struct fstab_rec* fstab_entry,
- const AvbHashtreeDescriptor& hashtree_desc,
- const std::string& salt, const std::string& root_digest,
- bool wait_for_verity_dev) {
- android::dm::DmTable table;
- if (!construct_verity_table(hashtree_desc, salt, root_digest, fstab_entry->blk_device, &table) ||
- !table.valid()) {
- LERROR << "Failed to construct verity table.";
- return false;
- }
- table.set_readonly(true);
-
- const std::string mount_point(basename(fstab_entry->mount_point));
- android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (!dm.CreateDevice(mount_point, table)) {
- LERROR << "Couldn't create verity device!";
- return false;
- }
-
- std::string dev_path;
- if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
- LERROR << "Couldn't get verity device path!";
- return false;
- }
-
- // Marks the underlying block device as read-only.
- fs_mgr_set_blk_ro(fstab_entry->blk_device);
-
- // Updates fstab_rec->blk_device to verity device name.
- free(fstab_entry->blk_device);
- fstab_entry->blk_device = strdup(dev_path.c_str());
-
- // Makes sure we've set everything up properly.
- if (wait_for_verity_dev && !fs_mgr_wait_for_file(dev_path, 1s)) {
- return false;
- }
-
- return true;
-}
-
-static bool get_hashtree_descriptor(const std::string& partition_name,
- const AvbSlotVerifyData& verify_data,
- AvbHashtreeDescriptor* out_hashtree_desc, std::string* out_salt,
- std::string* out_digest) {
- bool found = false;
- const uint8_t* desc_partition_name;
-
- for (size_t i = 0; i < verify_data.num_vbmeta_images && !found; i++) {
- // Get descriptors from vbmeta_images[i].
- size_t num_descriptors;
- std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
- avb_descriptor_get_all(verify_data.vbmeta_images[i].vbmeta_data,
- verify_data.vbmeta_images[i].vbmeta_size, &num_descriptors),
- avb_free);
-
- if (!descriptors || num_descriptors < 1) {
- continue;
- }
-
- // Ensures that hashtree descriptor is in /vbmeta or /boot or in
- // the same partition for verity setup.
- std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
- if (vbmeta_partition_name != "vbmeta" &&
- vbmeta_partition_name != "boot" && // for legacy device to append top-level vbmeta
- vbmeta_partition_name != partition_name) {
- LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
- << " for partition: " << partition_name.c_str();
- continue;
- }
-
- for (size_t j = 0; j < num_descriptors && !found; j++) {
- AvbDescriptor desc;
- if (!avb_descriptor_validate_and_byteswap(descriptors[j], &desc)) {
- LWARNING << "Descriptor[" << j << "] is invalid";
- continue;
- }
- if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
- desc_partition_name = (const uint8_t*)descriptors[j] + sizeof(AvbHashtreeDescriptor);
- if (!avb_hashtree_descriptor_validate_and_byteswap(
- (AvbHashtreeDescriptor*)descriptors[j], out_hashtree_desc)) {
- continue;
- }
- if (out_hashtree_desc->partition_name_len != partition_name.length()) {
- continue;
- }
- // Notes that desc_partition_name is not NUL-terminated.
- std::string hashtree_partition_name((const char*)desc_partition_name,
- out_hashtree_desc->partition_name_len);
- if (hashtree_partition_name == partition_name) {
- found = true;
- }
- }
- }
- }
-
- if (!found) {
- LERROR << "Partition descriptor not found: " << partition_name.c_str();
- return false;
- }
-
- const uint8_t* desc_salt = desc_partition_name + out_hashtree_desc->partition_name_len;
- *out_salt = bytes_to_hex(desc_salt, out_hashtree_desc->salt_len);
-
- const uint8_t* desc_digest = desc_salt + out_hashtree_desc->salt_len;
- *out_digest = bytes_to_hex(desc_digest, out_hashtree_desc->root_digest_len);
-
- return true;
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
- FsManagerAvbOps avb_ops(fstab);
- return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::Open(ByNameSymlinkMap&& by_name_symlink_map) {
- if (by_name_symlink_map.empty()) {
- LERROR << "Empty by_name_symlink_map when opening FsManagerAvbHandle";
- return nullptr;
- }
- FsManagerAvbOps avb_ops(std::move(by_name_symlink_map));
- return DoOpen(&avb_ops);
-}
-
-FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
- bool is_device_unlocked = fs_mgr_is_device_unlocked();
-
- FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
- if (!avb_handle) {
- LERROR << "Failed to allocate FsManagerAvbHandle";
- return nullptr;
- }
-
- AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
- : AVB_SLOT_VERIFY_FLAGS_NONE;
- AvbSlotVerifyResult verify_result =
- avb_ops->AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->avb_slot_data_);
-
- // Only allow two verify results:
- // - AVB_SLOT_VERIFY_RESULT_OK.
- // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (for UNLOCKED state).
- // If the device is UNLOCKED, i.e., |allow_verification_error| is true for
- // AvbSlotVerify(), then the following return values are all non-fatal:
- // * AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION
- // * AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED
- // * AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
- // The latter two results were checked by bootloader prior to start fs_mgr so
- // we just need to handle the first result here. See *dummy* operations in
- // FsManagerAvbOps and the comments in external/avb/libavb/avb_slot_verify.h
- // for more details.
- switch (verify_result) {
- case AVB_SLOT_VERIFY_RESULT_OK:
- avb_handle->status_ = kAvbHandleSuccess;
- break;
- case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
- if (!is_device_unlocked) {
- LERROR << "ERROR_VERIFICATION isn't allowed when the device is LOCKED";
- return nullptr;
- }
- avb_handle->status_ = kAvbHandleVerificationError;
- break;
- default:
- LERROR << "avb_slot_verify failed, result: " << verify_result;
- return nullptr;
- }
-
- // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
- avb_handle->avb_version_ =
- android::base::StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
-
- // Checks whether FLAGS_VERIFICATION_DISABLED is set:
- // - Only the top-level vbmeta struct is read.
- // - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
- // and AVB HASHTREE descriptor(s).
- AvbVBMetaImageHeader vbmeta_header;
- avb_vbmeta_image_header_to_host_byte_order(
- (AvbVBMetaImageHeader*)avb_handle->avb_slot_data_->vbmeta_images[0].vbmeta_data,
- &vbmeta_header);
- bool verification_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
-
- if (verification_disabled) {
- avb_handle->status_ = kAvbHandleVerificationDisabled;
- } else {
- // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
- std::unique_ptr<FsManagerAvbVerifier> avb_verifier = FsManagerAvbVerifier::Create();
- if (!avb_verifier) {
- LERROR << "Failed to create FsManagerAvbVerifier";
- return nullptr;
- }
- if (!avb_verifier->VerifyVbmetaImages(*avb_handle->avb_slot_data_)) {
- LERROR << "VerifyVbmetaImages failed";
- return nullptr;
- }
-
- // Checks whether FLAGS_HASHTREE_DISABLED is set.
- bool hashtree_disabled =
- ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
- if (hashtree_disabled) {
- avb_handle->status_ = kAvbHandleHashtreeDisabled;
- }
- }
-
- LINFO << "Returning avb_handle with status: " << avb_handle->status_;
- return avb_handle;
-}
-
-SetUpAvbHashtreeResult FsManagerAvbHandle::SetUpAvbHashtree(struct fstab_rec* fstab_entry,
- bool wait_for_verity_dev) {
- if (!fstab_entry || status_ == kAvbHandleUninitialized || !avb_slot_data_ ||
- avb_slot_data_->num_vbmeta_images < 1) {
- return SetUpAvbHashtreeResult::kFail;
- }
-
- if (status_ == kAvbHandleHashtreeDisabled || status_ == kAvbHandleVerificationDisabled) {
- LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
- return SetUpAvbHashtreeResult::kDisabled;
- }
-
- // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
- // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
- std::string partition_name;
- if (fstab_entry->fs_mgr_flags & MF_LOGICAL) {
- partition_name = fstab_entry->logical_partition_name;
- } else {
- partition_name = basename(fstab_entry->blk_device);
- }
-
- if (fstab_entry->fs_mgr_flags & MF_SLOTSELECT) {
- auto ab_suffix = partition_name.rfind(fs_mgr_get_slot_suffix());
- if (ab_suffix != std::string::npos) {
- partition_name.erase(ab_suffix);
- }
- }
-
- AvbHashtreeDescriptor hashtree_descriptor;
- std::string salt;
- std::string root_digest;
- if (!get_hashtree_descriptor(partition_name, *avb_slot_data_, &hashtree_descriptor, &salt,
- &root_digest)) {
- return SetUpAvbHashtreeResult::kFail;
- }
-
- // Converts HASHTREE descriptor to verity_table_params.
- if (!hashtree_dm_verity_setup(fstab_entry, hashtree_descriptor, salt, root_digest,
- wait_for_verity_dev)) {
- return SetUpAvbHashtreeResult::kFail;
- }
-
- return SetUpAvbHashtreeResult::kSuccess;
-}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 733ad55..abece4d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -81,6 +81,9 @@
bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
std::string cmdline;
if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
+ if (!cmdline.empty() && cmdline.back() == '\n') {
+ cmdline.pop_back();
+ }
return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 4dacebf..04ba0bf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -38,6 +38,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
#include <liblp/reader.h>
#include "fs_mgr_priv.h"
@@ -51,12 +52,20 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
- std::string* result) {
+static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+ const LpMetadataBlockDevice& block_device,
+ const std::string& super_device,
+ std::string* result) {
// Note: device-mapper will not accept symlinks, so we must use realpath
// here.
std::string name = GetBlockDevicePartitionName(block_device);
std::string path = "/dev/block/by-name/" + name;
+ // If the super device is the source of this block device's metadata,
+ // make sure we use the correct super device (and not just "super",
+ // which might not exist.)
+ if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
+ path = super_device;
+ }
if (!android::base::Realpath(path, result)) {
PERROR << "realpath: " << path;
return false;
@@ -65,7 +74,7 @@
}
static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
- DmTable* table) {
+ const std::string& super_device, DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -77,7 +86,7 @@
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = metadata.block_devices[extent.target_source];
std::string path;
- if (!GetPhysicalPartitionDevicePath(block_device, &path)) {
+ if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
@@ -102,30 +111,20 @@
static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
bool force_writable, const std::chrono::milliseconds& timeout_ms,
- std::string* path) {
+ const std::string& super_device, std::string* path) {
DeviceMapper& dm = DeviceMapper::Instance();
DmTable table;
- if (!CreateDmTable(metadata, partition, &table)) {
+ if (!CreateDmTable(metadata, partition, super_device, &table)) {
return false;
}
if (force_writable) {
table.set_readonly(false);
}
std::string name = GetPartitionName(partition);
- if (!dm.CreateDevice(name, table)) {
+ if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
- if (!dm.GetDmDevicePathByName(name, path)) {
- return false;
- }
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- if (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) {
- DestroyLogicalPartition(name, {});
- LERROR << "Timed out waiting for device path: " << *path;
- return false;
- }
- }
LINFO << "Created logical partition " << name << " on device " << *path;
return true;
}
@@ -137,13 +136,22 @@
LOG(ERROR) << "Could not read partition table.";
return true;
}
- for (const auto& partition : metadata->partitions) {
+ return CreateLogicalPartitions(*metadata.get(), block_device);
+}
+
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ return ReadMetadata(block_device.c_str(), slot);
+}
+
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
+ for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
std::string path;
- if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
+ if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@@ -151,6 +159,19 @@
return true;
}
+bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+ const std::string& partition_name, bool force_writable,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
+ for (const auto& partition : metadata.partitions) {
+ if (GetPartitionName(partition) == partition_name) {
+ return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms,
+ block_device, path);
+ }
+ }
+ LERROR << "Could not find any partition with name: " << partition_name;
+ return false;
+}
+
bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
const std::string& partition_name, bool force_writable,
const std::chrono::milliseconds& timeout_ms, std::string* path) {
@@ -159,27 +180,20 @@
LOG(ERROR) << "Could not read partition table.";
return true;
}
- for (const auto& partition : metadata->partitions) {
- if (GetPartitionName(partition) == partition_name) {
- return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
- path);
- }
- }
- LERROR << "Could not find any partition with name: " << partition_name;
- return false;
+ return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
+ timeout_ms, path);
}
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+bool UnmapDevice(const std::string& name) {
DeviceMapper& dm = DeviceMapper::Instance();
- std::string path;
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- dm.GetDmDevicePathByName(name, &path);
- }
if (!dm.DeleteDevice(name)) {
return false;
}
- if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) {
- LERROR << "Timed out waiting for device path to unlink: " << path;
+ return true;
+}
+
+bool DestroyLogicalPartition(const std::string& name) {
+ if (!UnmapDevice(name)) {
return false;
}
LINFO << "Unmapped logical partition " << name;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 845cca9..1c6652a 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,6 +24,7 @@
#include <cutils/partition_utils.h>
#include <sys/mount.h>
+#include <android-base/unique_fd.h>
#include <ext4_utils/ext4.h>
#include <ext4_utils/ext4_utils.h>
#include <logwrap/logwrap.h>
@@ -34,30 +35,31 @@
#include "fs_mgr_priv.h"
#include "cryptfs.h"
-static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
-{
- int fd;
+using android::base::unique_fd;
- if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+static int get_dev_sz(const std::string& fs_blkdev, uint64_t* dev_sz) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(fs_blkdev.c_str(), O_RDONLY | O_CLOEXEC)));
+
+ if (fd < 0) {
PERROR << "Cannot open block device";
return -1;
}
if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
PERROR << "Cannot get block device size";
- close(fd);
return -1;
}
- close(fd);
return 0;
}
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
-{
+static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,
+ bool crypt_footer) {
uint64_t dev_sz;
int rc = 0;
- int status;
rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -71,7 +73,8 @@
std::string size_str = std::to_string(dev_sz / 4096);
const char* const mke2fs_args[] = {
- "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+ "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev.c_str(),
+ size_str.c_str(), nullptr};
rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
true, LOG_KLOG, true, nullptr, nullptr, 0);
@@ -81,12 +84,7 @@
}
const char* const e2fsdroid_args[] = {
- "/system/bin/e2fsdroid",
- "-e",
- "-a",
- fs_mnt_point,
- fs_blkdev,
- nullptr};
+ "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
@@ -97,10 +95,7 @@
return rc;
}
-static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
-{
- int status;
-
+static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer) {
if (!dev_sz) {
int rc = get_dev_sz(fs_blkdev, &dev_sz);
if (rc) {
@@ -117,13 +112,8 @@
// clang-format off
const char* const args[] = {
"/system/bin/make_f2fs",
- "-d1",
- "-f",
- "-O", "encrypt",
- "-O", "quota",
- "-O", "verity",
- "-w", "4096",
- fs_blkdev,
+ "-g", "android",
+ fs_blkdev.c_str(),
size_str.c_str(),
nullptr
};
@@ -133,20 +123,15 @@
LOG_KLOG, true, nullptr, nullptr, 0);
}
-int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
-{
- int rc = -EINVAL;
+int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
+ LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
- LERROR << __FUNCTION__ << ": Format " << fstab->blk_device
- << " as '" << fstab->fs_type << "'";
-
- if (!strncmp(fstab->fs_type, "f2fs", 4)) {
- rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
- } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
- rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
+ if (entry.fs_type == "f2fs") {
+ return format_f2fs(entry.blk_device, entry.length, crypt_footer);
+ } else if (entry.fs_type == "ext4") {
+ return format_ext4(entry.blk_device, entry.mount_point, crypt_footer);
} else {
- LERROR << "File system type '" << fstab->fs_type << "' is not supported";
+ LERROR << "File system type '" << entry.fs_type << "' is not supported";
+ return -EINVAL;
}
-
- return rc;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index fc3a05c..9a0f4fe 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -24,145 +24,56 @@
#include <unistd.h>
#include <algorithm>
+#include <array>
#include <utility>
#include <vector>
#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <libgsi/libgsi.h>
#include "fs_mgr_priv.h"
+using android::base::ParseByteCount;
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
using android::base::StartsWith;
+namespace android {
+namespace fs_mgr {
+namespace {
+
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
-struct fs_mgr_flag_values {
- char *key_loc;
- char* key_dir;
- char *verity_loc;
- char *sysfs_path;
- long long part_length;
- char *label;
- int partnum;
- int swap_prio;
- int max_comp_streams;
- unsigned int zram_size;
- uint64_t reserved_size;
- unsigned int file_contents_mode;
- unsigned int file_names_mode;
- unsigned int erase_blk_size;
- unsigned int logical_blk_size;
-};
-
-struct flag_list {
+struct FlagList {
const char *name;
- unsigned int flag;
+ uint64_t flag;
};
-static struct flag_list mount_flags[] = {
- { "noatime", MS_NOATIME },
- { "noexec", MS_NOEXEC },
- { "nosuid", MS_NOSUID },
- { "nodev", MS_NODEV },
- { "nodiratime", MS_NODIRATIME },
- { "ro", MS_RDONLY },
- { "rw", 0 },
- { "remount", MS_REMOUNT },
- { "bind", MS_BIND },
- { "rec", MS_REC },
- { "unbindable", MS_UNBINDABLE },
- { "private", MS_PRIVATE },
- { "slave", MS_SLAVE },
- { "shared", MS_SHARED },
- { "defaults", 0 },
- { 0, 0 },
-};
-
-static struct flag_list fs_mgr_flags[] = {
- {"wait", MF_WAIT},
- {"check", MF_CHECK},
- {"encryptable=", MF_CRYPT},
- {"forceencrypt=", MF_FORCECRYPT},
- {"fileencryption=", MF_FILEENCRYPTION},
- {"forcefdeorfbe=", MF_FORCEFDEORFBE},
- {"keydirectory=", MF_KEYDIRECTORY},
- {"nonremovable", MF_NONREMOVABLE},
- {"voldmanaged=", MF_VOLDMANAGED},
- {"length=", MF_LENGTH},
- {"recoveryonly", MF_RECOVERYONLY},
- {"swapprio=", MF_SWAPPRIO},
- {"zramsize=", MF_ZRAMSIZE},
- {"max_comp_streams=", MF_MAX_COMP_STREAMS},
- {"verifyatboot", MF_VERIFYATBOOT},
- {"verify", MF_VERIFY},
- {"avb", MF_AVB},
- {"noemulatedsd", MF_NOEMULATEDSD},
- {"notrim", MF_NOTRIM},
- {"formattable", MF_FORMATTABLE},
- {"slotselect", MF_SLOTSELECT},
- {"nofail", MF_NOFAIL},
- {"latemount", MF_LATEMOUNT},
- {"reservedsize=", MF_RESERVEDSIZE},
- {"quota", MF_QUOTA},
- {"eraseblk=", MF_ERASEBLKSIZE},
- {"logicalblk=", MF_LOGICALBLKSIZE},
- {"sysfs_path=", MF_SYSFS},
+FlagList kMountFlagsList[] = {
+ {"noatime", MS_NOATIME},
+ {"noexec", MS_NOEXEC},
+ {"nosuid", MS_NOSUID},
+ {"nodev", MS_NODEV},
+ {"nodiratime", MS_NODIRATIME},
+ {"ro", MS_RDONLY},
+ {"rw", 0},
+ {"sync", MS_SYNCHRONOUS},
+ {"remount", MS_REMOUNT},
+ {"bind", MS_BIND},
+ {"rec", MS_REC},
+ {"unbindable", MS_UNBINDABLE},
+ {"private", MS_PRIVATE},
+ {"slave", MS_SLAVE},
+ {"shared", MS_SHARED},
{"defaults", 0},
- {"logical", MF_LOGICAL},
- {"checkpoint=block", MF_CHECKPOINT_BLK},
- {"checkpoint=fs", MF_CHECKPOINT_FS},
- {0, 0},
};
-#define EM_AES_256_XTS 1
-#define EM_ICE 2
-#define EM_AES_256_CTS 3
-#define EM_AES_256_HEH 4
-
-static const struct flag_list file_contents_encryption_modes[] = {
- {"aes-256-xts", EM_AES_256_XTS},
- {"software", EM_AES_256_XTS}, /* alias for backwards compatibility */
- {"ice", EM_ICE}, /* hardware-specific inline cryptographic engine */
- {0, 0},
-};
-
-static const struct flag_list file_names_encryption_modes[] = {
- {"aes-256-cts", EM_AES_256_CTS},
- {"aes-256-heh", EM_AES_256_HEH},
- {0, 0},
-};
-
-static unsigned int encryption_mode_to_flag(const struct flag_list *list,
- const char *mode, const char *type)
-{
- const struct flag_list *j;
-
- for (j = list; j->name; ++j) {
- if (!strcmp(mode, j->name)) {
- return j->flag;
- }
- }
- LERROR << "Unknown " << type << " encryption mode: " << mode;
- return 0;
-}
-
-static const char *flag_to_encryption_mode(const struct flag_list *list,
- unsigned int flag)
-{
- const struct flag_list *j;
-
- for (j = list; j->name; ++j) {
- if (flag == j->flag) {
- return j->name;
- }
- }
- return nullptr;
-}
-
-static uint64_t calculate_zram_size(unsigned int percentage)
-{
- uint64_t total;
+off64_t CalculateZramSize(int percentage) {
+ off64_t total;
total = sysconf(_SC_PHYS_PAGES);
total *= percentage;
@@ -173,30 +84,12 @@
return total;
}
-static uint64_t parse_size(const char *arg)
-{
- char *endptr;
- uint64_t size = strtoull(arg, &endptr, 10);
- if (*endptr == 'k' || *endptr == 'K')
- size *= 1024LL;
- else if (*endptr == 'm' || *endptr == 'M')
- size *= 1024LL * 1024LL;
- else if (*endptr == 'g' || *endptr == 'G')
- size *= 1024LL * 1024LL * 1024LL;
-
- return size;
-}
-
-/* fills 'dt_value' with the underlying device tree value string without
- * the trailing '\0'. Returns true if 'dt_value' has a valid string, 'false'
- * otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value)
-{
+// Fills 'dt_value' with the underlying device tree value string without the trailing '\0'.
+// Returns true if 'dt_value' has a valid string, 'false' otherwise.
+bool ReadDtFile(const std::string& file_name, std::string* dt_value) {
if (android::base::ReadFileToString(file_name, dt_value)) {
if (!dt_value->empty()) {
- // trim the trailing '\0' out, otherwise the comparison
- // will produce false-negatives.
+ // Trim the trailing '\0' out, otherwise the comparison will produce false-negatives.
dt_value->resize(dt_value->size() - 1);
return true;
}
@@ -205,185 +98,252 @@
return false;
}
-static int parse_flags(char *flags, struct flag_list *fl,
- struct fs_mgr_flag_values *flag_vals,
- char *fs_options, int fs_options_len)
-{
- int f = 0;
- int i;
- char *p;
- char *savep;
+const std::array<const char*, 3> kFileContentsEncryptionMode = {
+ "aes-256-xts",
+ "adiantum",
+ "ice",
+};
- /* initialize flag values. If we find a relevant flag, we'll
- * update the value */
- if (flag_vals) {
- memset(flag_vals, 0, sizeof(*flag_vals));
- flag_vals->partnum = -1;
- flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
+const std::array<const char*, 3> kFileNamesEncryptionMode = {
+ "aes-256-cts",
+ "aes-256-heh",
+ "adiantum",
+};
+
+void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
+ // The fileencryption flag is followed by an = and the mode of contents encryption, then
+ // optionally a and the mode of filenames encryption (defaults to aes-256-cts). Get it and
+ // return it.
+ entry->fs_mgr_flags.file_encryption = true;
+
+ auto parts = Split(arg, ":");
+ if (parts.empty() || parts.size() > 2) {
+ LWARNING << "Warning: fileencryption= flag malformed: " << arg;
+ return;
}
- /* initialize fs_options to the null string */
- if (fs_options && (fs_options_len > 0)) {
- fs_options[0] = '\0';
+ // Alias for backwards compatibility.
+ if (parts[0] == "software") {
+ parts[0] = "aes-256-xts";
}
- p = strtok_r(flags, ",", &savep);
- while (p) {
- /* Look for the flag "p" in the flag list "fl"
- * If not found, the loop exits with fl[i].name being null.
- */
- for (i = 0; fl[i].name; i++) {
- auto name = fl[i].name;
- auto len = strlen(name);
- auto end = len;
- if (name[end - 1] == '=') --end;
- if (!strncmp(p, name, len) && (p[end] == name[end])) {
- f |= fl[i].flag;
- if (!flag_vals) break;
- if (p[end] != '=') break;
- char* arg = p + end + 1;
- auto flag = fl[i].flag;
- if (flag == MF_CRYPT) {
- /* The encryptable flag is followed by an = and the
- * location of the keys. Get it and return it.
- */
- flag_vals->key_loc = strdup(arg);
- } else if (flag == MF_VERIFY) {
- /* If the verify flag is followed by an = and the
- * location for the verity state, get it and return it.
- */
- flag_vals->verity_loc = strdup(arg);
- } else if (flag == MF_FORCECRYPT) {
- /* The forceencrypt flag is followed by an = and the
- * location of the keys. Get it and return it.
- */
- flag_vals->key_loc = strdup(arg);
- } else if (flag == MF_FORCEFDEORFBE) {
- /* The forcefdeorfbe flag is followed by an = and the
- * location of the keys. Get it and return it.
- */
- flag_vals->key_loc = strdup(arg);
- flag_vals->file_contents_mode = EM_AES_256_XTS;
- flag_vals->file_names_mode = EM_AES_256_CTS;
- } else if (flag == MF_FILEENCRYPTION) {
- /* The fileencryption flag is followed by an = and
- * the mode of contents encryption, then optionally a
- * : and the mode of filenames encryption (defaults
- * to aes-256-cts). Get it and return it.
- */
- auto mode = arg;
- auto colon = strchr(mode, ':');
- if (colon) {
- *colon = '\0';
- }
- flag_vals->file_contents_mode =
- encryption_mode_to_flag(file_contents_encryption_modes,
- mode, "file contents");
- if (colon) {
- flag_vals->file_names_mode =
- encryption_mode_to_flag(file_names_encryption_modes,
- colon + 1, "file names");
- } else {
- flag_vals->file_names_mode = EM_AES_256_CTS;
- }
- } else if (flag == MF_KEYDIRECTORY) {
- /* The metadata flag is followed by an = and the
- * directory for the keys. Get it and return it.
- */
- flag_vals->key_dir = strdup(arg);
- } else if (flag == MF_LENGTH) {
- /* The length flag is followed by an = and the
- * size of the partition. Get it and return it.
- */
- flag_vals->part_length = strtoll(arg, NULL, 0);
- } else if (flag == MF_VOLDMANAGED) {
- /* The voldmanaged flag is followed by an = and the
- * label, a colon and the partition number or the
- * word "auto", e.g.
- * voldmanaged=sdcard:3
- * Get and return them.
- */
- auto label_start = arg;
- auto label_end = strchr(label_start, ':');
+ if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
+ parts[0]) == kFileContentsEncryptionMode.end()) {
+ LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
+ << arg;
+ return;
+ }
- if (label_end) {
- flag_vals->label = strndup(label_start,
- (int) (label_end - label_start));
- auto part_start = label_end + 1;
- if (!strcmp(part_start, "auto")) {
- flag_vals->partnum = -1;
- } else {
- flag_vals->partnum = strtol(part_start, NULL, 0);
- }
- } else {
- LERROR << "Warning: voldmanaged= flag malformed";
- }
- } else if (flag == MF_SWAPPRIO) {
- flag_vals->swap_prio = strtoll(arg, NULL, 0);
- } else if (flag == MF_MAX_COMP_STREAMS) {
- flag_vals->max_comp_streams = strtoll(arg, NULL, 0);
- } else if (flag == MF_ZRAMSIZE) {
- auto is_percent = !!strrchr(arg, '%');
- auto val = strtoll(arg, NULL, 0);
- if (is_percent)
- flag_vals->zram_size = calculate_zram_size(val);
- else
- flag_vals->zram_size = val;
- } else if (flag == MF_RESERVEDSIZE) {
- /* The reserved flag is followed by an = and the
- * reserved size of the partition. Get it and return it.
- */
- flag_vals->reserved_size = parse_size(arg);
- } else if (flag == MF_ERASEBLKSIZE) {
- /* The erase block size flag is followed by an = and the flash
- * erase block size. Get it, check that it is a power of 2 and
- * at least 4096, and return it.
- */
- auto val = strtoul(arg, NULL, 0);
- if (val >= 4096 && (val & (val - 1)) == 0)
- flag_vals->erase_blk_size = val;
- } else if (flag == MF_LOGICALBLKSIZE) {
- /* The logical block size flag is followed by an = and the flash
- * logical block size. Get it, check that it is a power of 2 and
- * at least 4096, and return it.
- */
- auto val = strtoul(arg, NULL, 0);
- if (val >= 4096 && (val & (val - 1)) == 0)
- flag_vals->logical_blk_size = val;
- } else if (flag == MF_SYSFS) {
- /* The path to trigger device gc by idle-maint of vold. */
- flag_vals->sysfs_path = strdup(arg);
- }
- break;
- }
+ entry->file_contents_mode = parts[0];
+
+ if (parts.size() == 2) {
+ if (std::find(kFileNamesEncryptionMode.begin(), kFileNamesEncryptionMode.end(), parts[1]) ==
+ kFileNamesEncryptionMode.end()) {
+ LWARNING << "fileencryption= flag malformed, file names encryption mode not found: "
+ << arg;
+ return;
}
- if (!fl[i].name) {
- if (fs_options) {
- /* It's not a known flag, so it must be a filesystem specific
- * option. Add it to fs_options if it was passed in.
- */
- strlcat(fs_options, p, fs_options_len);
- strlcat(fs_options, ",", fs_options_len);
- } else {
- /* fs_options was not passed in, so if the flag is unknown
- * it's an error.
- */
- LERROR << "Warning: unknown flag " << p;
- }
- }
- p = strtok_r(NULL, ",", &savep);
+ entry->file_names_mode = parts[1];
+ } else if (entry->file_contents_mode == "adiantum") {
+ entry->file_names_mode = "adiantum";
+ } else {
+ entry->file_names_mode = "aes-256-cts";
}
-
- if (fs_options && fs_options[0]) {
- /* remove the last trailing comma from the list of options */
- fs_options[strlen(fs_options) - 1] = '\0';
- }
-
- return f;
}
-static std::string init_android_dt_dir() {
+bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
+ for (const auto& [name, value] : kMountFlagsList) {
+ if (flag == name) {
+ entry->flags |= value;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ParseMountFlags(const std::string& flags, FstabEntry* entry) {
+ std::string fs_options;
+ for (const auto& flag : Split(flags, ",")) {
+ if (!SetMountFlag(flag, entry)) {
+ // Unknown flag, so it must be a filesystem specific option.
+ if (!fs_options.empty()) {
+ fs_options.append(","); // appends a comma if not the first
+ }
+ fs_options.append(flag);
+
+ if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
+ std::string arg;
+ if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+ arg = flag.substr(equal_sign + 1);
+ }
+ if (!ParseInt(arg, &entry->reserved_size)) {
+ LWARNING << "Warning: reserve_root= flag malformed: " << arg;
+ } else {
+ entry->reserved_size <<= 12;
+ }
+ }
+ }
+ }
+ entry->fs_options = std::move(fs_options);
+}
+
+void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
+ for (const auto& flag : Split(flags, ",")) {
+ if (flag.empty() || flag == "defaults") continue;
+ std::string arg;
+ if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+ arg = flag.substr(equal_sign + 1);
+ }
+
+ // First handle flags that simply set a boolean.
+#define CheckFlag(flag_name, value) \
+ if (flag == flag_name) { \
+ entry->fs_mgr_flags.value = true; \
+ continue; \
+ }
+
+ CheckFlag("wait", wait);
+ CheckFlag("check", check);
+ CheckFlag("nonremovable", nonremovable);
+ CheckFlag("recoveryonly", recovery_only);
+ CheckFlag("noemulatedsd", no_emulated_sd);
+ CheckFlag("notrim", no_trim);
+ CheckFlag("verify", verify);
+ CheckFlag("formattable", formattable);
+ CheckFlag("slotselect", slot_select);
+ CheckFlag("latemount", late_mount);
+ CheckFlag("nofail", no_fail);
+ CheckFlag("verifyatboot", verify_at_boot);
+ CheckFlag("quota", quota);
+ CheckFlag("avb", avb);
+ CheckFlag("logical", logical);
+ CheckFlag("checkpoint=block", checkpoint_blk);
+ CheckFlag("checkpoint=fs", checkpoint_fs);
+ CheckFlag("first_stage_mount", first_stage_mount);
+ CheckFlag("slotselect_other", slot_select_other);
+ CheckFlag("fsverity", fs_verity);
+
+#undef CheckFlag
+
+ // Then handle flags that take an argument.
+ if (StartsWith(flag, "encryptable=")) {
+ // The encryptable flag is followed by an = and the location of the keys.
+ entry->fs_mgr_flags.crypt = true;
+ entry->key_loc = arg;
+ } else if (StartsWith(flag, "voldmanaged=")) {
+ // The voldmanaged flag is followed by an = and the label, a colon and the partition
+ // number or the word "auto", e.g. voldmanaged=sdcard:3
+ entry->fs_mgr_flags.vold_managed = true;
+ auto parts = Split(arg, ":");
+ if (parts.size() != 2) {
+ LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
+ continue;
+ }
+
+ entry->label = std::move(parts[0]);
+ if (parts[1] == "auto") {
+ entry->partnum = -1;
+ } else {
+ if (!ParseInt(parts[1], &entry->partnum)) {
+ entry->partnum = -1;
+ LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
+ continue;
+ }
+ }
+ } else if (StartsWith(flag, "length=")) {
+ // The length flag is followed by an = and the size of the partition.
+ if (!ParseInt(arg, &entry->length)) {
+ LWARNING << "Warning: length= flag malformed: " << arg;
+ }
+ } else if (StartsWith(flag, "swapprio=")) {
+ if (!ParseInt(arg, &entry->swap_prio)) {
+ LWARNING << "Warning: length= flag malformed: " << arg;
+ }
+ } else if (StartsWith(flag, "zramsize=")) {
+ if (!arg.empty() && arg.back() == '%') {
+ arg.pop_back();
+ int val;
+ if (ParseInt(arg, &val, 0, 100)) {
+ entry->zram_size = CalculateZramSize(val);
+ } else {
+ LWARNING << "Warning: zramsize= flag malformed: " << arg;
+ }
+ } else {
+ if (!ParseInt(arg, &entry->zram_size)) {
+ LWARNING << "Warning: zramsize= flag malformed: " << arg;
+ }
+ }
+ } else if (StartsWith(flag, "forceencrypt=")) {
+ // The forceencrypt flag is followed by an = and the location of the keys.
+ entry->fs_mgr_flags.force_crypt = true;
+ entry->key_loc = arg;
+ } else if (StartsWith(flag, "fileencryption=")) {
+ ParseFileEncryption(arg, entry);
+ } else if (StartsWith(flag, "forcefdeorfbe=")) {
+ // The forcefdeorfbe flag is followed by an = and the location of the keys. Get it and
+ // return it.
+ entry->fs_mgr_flags.force_fde_or_fbe = true;
+ entry->key_loc = arg;
+ entry->file_contents_mode = "aes-256-xts";
+ entry->file_names_mode = "aes-256-cts";
+ } else if (StartsWith(flag, "max_comp_streams=")) {
+ if (!ParseInt(arg, &entry->max_comp_streams)) {
+ LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
+ }
+ } else if (StartsWith(flag, "reservedsize=")) {
+ // The reserved flag is followed by an = and the reserved size of the partition.
+ uint64_t size;
+ if (!ParseByteCount(arg, &size)) {
+ LWARNING << "Warning: reservedsize= flag malformed: " << arg;
+ } else {
+ entry->reserved_size = static_cast<off64_t>(size);
+ }
+ } else if (StartsWith(flag, "eraseblk=")) {
+ // The erase block size flag is followed by an = and the flash erase block size. Get it,
+ // check that it is a power of 2 and at least 4096, and return it.
+ off64_t val;
+ if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
+ LWARNING << "Warning: eraseblk= flag malformed: " << arg;
+ } else {
+ entry->erase_blk_size = val;
+ }
+ } else if (StartsWith(flag, "logicalblk=")) {
+ // The logical block size flag is followed by an = and the flash logical block size. Get
+ // it, check that it is a power of 2 and at least 4096, and return it.
+ off64_t val;
+ if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
+ LWARNING << "Warning: logicalblk= flag malformed: " << arg;
+ } else {
+ entry->logical_blk_size = val;
+ }
+ } else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb"
+ entry->avb_keys = arg;
+ } else if (StartsWith(flag, "avb")) {
+ entry->fs_mgr_flags.avb = true;
+ entry->vbmeta_partition = arg;
+ } else if (StartsWith(flag, "keydirectory=")) {
+ // The metadata flag is followed by an = and the directory for the keys.
+ entry->key_dir = arg;
+ } else if (StartsWith(flag, "sysfs_path=")) {
+ // The path to trigger device gc by idle-maint of vold.
+ entry->sysfs_path = arg;
+ } else if (StartsWith(flag, "zram_loopback_path=")) {
+ // The path to use loopback for zram.
+ entry->zram_loopback_path = arg;
+ } else if (StartsWith(flag, "zram_loopback_size=")) {
+ if (!ParseByteCount(arg, &entry->zram_loopback_size)) {
+ LWARNING << "Warning: zram_loopback_size= flag malformed: " << arg;
+ }
+ } else if (StartsWith(flag, "zram_backing_dev_path=")) {
+ entry->zram_backing_dev_path = arg;
+ } else {
+ LWARNING << "Warning: unknown flag: " << flag;
+ }
+ }
+}
+
+std::string InitAndroidDtDir() {
std::string android_dt_dir;
// The platform may specify a custom Android DT path in kernel cmdline
if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
@@ -393,27 +353,23 @@
return android_dt_dir;
}
-// FIXME: The same logic is duplicated in system/core/init/
-const std::string& get_android_dt_dir() {
- // Set once and saves time for subsequent calls to this function
- static const std::string kAndroidDtDir = init_android_dt_dir();
- return kAndroidDtDir;
-}
-
-static bool is_dt_fstab_compatible() {
+bool IsDtFstabCompatible() {
std::string dt_value;
std::string file_name = get_android_dt_dir() + "/fstab/compatible";
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == "android,fstab") {
- return true;
- }
+
+ if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
+ // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
+ std::string status_value;
+ std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+ return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
+ status_value == "okay";
}
return false;
}
-static std::string read_fstab_from_dt() {
- if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+std::string ReadFstabFromDt() {
+ if (!is_dt_compatible() || !IsDtFstabCompatible()) {
return {};
}
@@ -434,7 +390,7 @@
std::string value;
// skip a partition entry if the status property is present and not set to ok
file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
- if (read_dt_file(file_name, &value)) {
+ if (ReadDtFile(file_name, &value)) {
if (value != "okay" && value != "ok") {
LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
continue;
@@ -442,7 +398,7 @@
}
file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
- if (!read_dt_file(file_name, &value)) {
+ if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
return {};
}
@@ -451,7 +407,7 @@
std::string mount_point;
file_name =
android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
- if (read_dt_file(file_name, &value)) {
+ if (ReadDtFile(file_name, &value)) {
LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
mount_point = value;
} else {
@@ -460,21 +416,21 @@
fstab_entry.push_back(mount_point);
file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
- if (!read_dt_file(file_name, &value)) {
+ if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
- if (!read_dt_file(file_name, &value)) {
+ if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
fstab_entry.push_back(value);
file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
- if (!read_dt_file(file_name, &value)) {
+ if (!ReadDtFile(file_name, &value)) {
LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
return {};
}
@@ -494,61 +450,33 @@
return fstab_result;
}
-bool is_dt_compatible() {
- std::string file_name = get_android_dt_dir() + "/compatible";
- std::string dt_value;
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == "android,firmware") {
- return true;
+// Identify path to fstab file. Lookup is based on pattern fstab.<hardware>,
+// fstab.<hardware.platform> in folders /odm/etc, vendor/etc, or /.
+std::string GetFstabPath() {
+ for (const char* prop : {"hardware", "hardware.platform"}) {
+ std::string hw;
+
+ if (!fs_mgr_get_boot_config(prop, &hw)) continue;
+
+ for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
+ std::string fstab_path = prefix + hw;
+ if (access(fstab_path.c_str(), F_OK) == 0) {
+ return fstab_path;
+ }
}
}
- return false;
+ return "";
}
-static struct fstab* fs_mgr_read_fstab_file(FILE* fstab_file, bool proc_mounts) {
- int cnt, entries;
+bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
ssize_t len;
size_t alloc_len = 0;
char *line = NULL;
const char *delim = " \t";
char *save_ptr, *p;
- struct fstab *fstab = NULL;
- struct fs_mgr_flag_values flag_vals;
-#define FS_OPTIONS_LEN 1024
- char tmp_fs_options[FS_OPTIONS_LEN];
+ Fstab fstab;
- entries = 0;
- while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
- /* if the last character is a newline, shorten the string by 1 byte */
- if (line[len - 1] == '\n') {
- line[len - 1] = '\0';
- }
- /* Skip any leading whitespace */
- p = line;
- while (isspace(*p)) {
- p++;
- }
- /* ignore comments or empty lines */
- if (*p == '#' || *p == '\0')
- continue;
- entries++;
- }
-
- if (!entries) {
- LERROR << "No entries found in fstab";
- goto err;
- }
-
- /* Allocate and init the fstab structure */
- fstab = static_cast<struct fstab *>(calloc(1, sizeof(struct fstab)));
- fstab->num_entries = entries;
- fstab->recs = static_cast<struct fstab_rec *>(
- calloc(fstab->num_entries, sizeof(struct fstab_rec)));
-
- fseek(fstab_file, 0, SEEK_SET);
-
- cnt = 0;
while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
/* if the last character is a newline, shorten the string by 1 byte */
if (line[len - 1] == '\n') {
@@ -564,47 +492,32 @@
if (*p == '#' || *p == '\0')
continue;
- /* If a non-comment entry is greater than the size we allocated, give an
- * error and quit. This can happen in the unlikely case the file changes
- * between the two reads.
- */
- if (cnt >= entries) {
- LERROR << "Tried to process more entries than counted";
- break;
- }
+ FstabEntry entry;
if (!(p = strtok_r(line, delim, &save_ptr))) {
LERROR << "Error parsing mount source";
goto err;
}
- fstab->recs[cnt].blk_device = strdup(p);
+ entry.blk_device = p;
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
LERROR << "Error parsing mount_point";
goto err;
}
- fstab->recs[cnt].mount_point = strdup(p);
+ entry.mount_point = p;
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
LERROR << "Error parsing fs_type";
goto err;
}
- fstab->recs[cnt].fs_type = strdup(p);
+ entry.fs_type = p;
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
LERROR << "Error parsing mount_flags";
goto err;
}
- tmp_fs_options[0] = '\0';
- fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
- tmp_fs_options, FS_OPTIONS_LEN);
- /* fs_options are optional */
- if (tmp_fs_options[0]) {
- fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
- } else {
- fstab->recs[cnt].fs_options = NULL;
- }
+ ParseMountFlags(p, &entry);
// For /proc/mounts, ignore everything after mnt_freq and mnt_passno
if (proc_mounts) {
@@ -613,78 +526,33 @@
LERROR << "Error parsing fs_mgr_options";
goto err;
}
- fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
- &flag_vals, NULL, 0);
- fstab->recs[cnt].key_loc = flag_vals.key_loc;
- fstab->recs[cnt].key_dir = flag_vals.key_dir;
- fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
- fstab->recs[cnt].length = flag_vals.part_length;
- fstab->recs[cnt].label = flag_vals.label;
- fstab->recs[cnt].partnum = flag_vals.partnum;
- fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
- fstab->recs[cnt].max_comp_streams = flag_vals.max_comp_streams;
- fstab->recs[cnt].zram_size = flag_vals.zram_size;
- fstab->recs[cnt].reserved_size = flag_vals.reserved_size;
- fstab->recs[cnt].file_contents_mode = flag_vals.file_contents_mode;
- fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
- fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
- fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
- fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
- if (fstab->recs[cnt].fs_mgr_flags & MF_LOGICAL) {
- fstab->recs[cnt].logical_partition_name = strdup(fstab->recs[cnt].blk_device);
+
+ ParseFsMgrFlags(p, &entry);
+
+ if (entry.fs_mgr_flags.logical) {
+ entry.logical_partition_name = entry.blk_device;
}
- cnt++;
+ fstab.emplace_back(std::move(entry));
}
+
+ if (fstab.empty()) {
+ LERROR << "No entries found in fstab";
+ goto err;
+ }
+
/* If an A/B partition, modify block device to be the real block device */
- if (!fs_mgr_update_for_slotselect(fstab)) {
+ if (!fs_mgr_update_for_slotselect(&fstab)) {
LERROR << "Error updating for slotselect";
goto err;
}
free(line);
- return fstab;
+ *fstab_out = std::move(fstab);
+ return true;
err:
free(line);
- if (fstab)
- fs_mgr_free_fstab(fstab);
- return NULL;
-}
-
-/* merges fstab entries from both a and b, then returns the merged result.
- * note that the caller should only manage the return pointer without
- * doing further memory management for the two inputs, i.e. only need to
- * frees up memory of the return value without touching a and b. */
-static struct fstab *in_place_merge(struct fstab *a, struct fstab *b)
-{
- if (!a && !b) return nullptr;
- if (!a) return b;
- if (!b) return a;
-
- int total_entries = a->num_entries + b->num_entries;
- a->recs = static_cast<struct fstab_rec *>(realloc(
- a->recs, total_entries * (sizeof(struct fstab_rec))));
- if (!a->recs) {
- LERROR << __FUNCTION__ << "(): failed to allocate fstab recs";
- // If realloc() fails the original block is left untouched;
- // it is not freed or moved. So we have to free both a and b here.
- fs_mgr_free_fstab(a);
- fs_mgr_free_fstab(b);
- return nullptr;
- }
-
- for (int i = a->num_entries, j = 0; i < total_entries; i++, j++) {
- // Copy the structs by assignment.
- a->recs[i] = b->recs[j];
- }
-
- // We can't call fs_mgr_free_fstab because a->recs still references the
- // memory allocated by strdup.
- free(b->recs);
- free(b);
-
- a->num_entries = total_entries;
- return a;
+ return false;
}
/* Extracts <device>s from the by-name symlinks specified in a fstab:
@@ -697,11 +565,11 @@
* /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
* it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
*/
-static std::set<std::string> extract_boot_devices(const fstab& fstab) {
+std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
std::set<std::string> boot_devices;
- for (int i = 0; i < fstab.num_entries; i++) {
- std::string blk_device(fstab.recs[i].blk_device);
+ for (const auto& entry : fstab) {
+ std::string blk_device = entry.blk_device;
// Skips blk_device that doesn't conform to the format.
if (!android::base::StartsWith(blk_device, "/dev/block") ||
android::base::StartsWith(blk_device, "/dev/block/by-name") ||
@@ -730,289 +598,253 @@
return boot_devices;
}
-struct fstab *fs_mgr_read_fstab(const char *fstab_path)
-{
- struct fstab *fstab;
+FstabEntry BuildGsiUserdataFstabEntry() {
+ constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
- auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fstab_path, "re"), fclose};
- if (!fstab_file) {
- PERROR << __FUNCTION__<< "(): cannot open file: '" << fstab_path << "'";
- return nullptr;
- }
-
- fstab = fs_mgr_read_fstab_file(fstab_file.get(), !strcmp("/proc/mounts", fstab_path));
- if (!fstab) {
- LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << fstab_path << "'";
- }
-
- return fstab;
+ FstabEntry userdata = {
+ .blk_device = "userdata_gsi",
+ .mount_point = "/data",
+ .fs_type = "ext4",
+ .flags = kFlags,
+ .reserved_size = 128 * 1024 * 1024,
+ };
+ userdata.fs_mgr_flags.wait = true;
+ userdata.fs_mgr_flags.check = true;
+ userdata.fs_mgr_flags.logical = true;
+ userdata.fs_mgr_flags.quota = true;
+ userdata.fs_mgr_flags.late_mount = true;
+ userdata.fs_mgr_flags.formattable = true;
+ return userdata;
}
-/* Returns fstab entries parsed from the device tree if they
- * exist
- */
-struct fstab *fs_mgr_read_fstab_dt()
-{
- std::string fstab_buf = read_fstab_from_dt();
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+ auto iter = std::remove_if(fstab->begin(), fstab->end(),
+ [&](const auto& entry) { return entry.mount_point == mount_point; });
+ if (iter != fstab->end()) {
+ fstab->erase(iter, fstab->end());
+ return true;
+ }
+ return false;
+}
+
+void TransformFstabForGsi(Fstab* fstab) {
+ // Inherit fstab properties for userdata.
+ FstabEntry userdata;
+ if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+ userdata = *entry;
+ userdata.blk_device = "userdata_gsi";
+ userdata.fs_mgr_flags.logical = true;
+ userdata.fs_mgr_flags.formattable = true;
+ if (!userdata.key_dir.empty()) {
+ userdata.key_dir += "/gsi";
+ }
+ } else {
+ userdata = BuildGsiUserdataFstabEntry();
+ }
+
+ if (EraseFstabEntry(fstab, "/system")) {
+ fstab->emplace_back(BuildGsiSystemFstabEntry());
+ }
+
+ if (EraseFstabEntry(fstab, "/data")) {
+ fstab->emplace_back(userdata);
+ }
+}
+
+} // namespace
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+ auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!fstab_file) {
+ PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+ return false;
+ }
+
+ bool is_proc_mounts = path == "/proc/mounts";
+
+ if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, fstab)) {
+ LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
+ return false;
+ }
+ if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+ TransformFstabForGsi(fstab);
+ }
+
+ SkipMountingPartitions(fstab);
+
+ return true;
+}
+
+// Returns fstab entries parsed from the device tree if they exist
+bool ReadFstabFromDt(Fstab* fstab, bool log) {
+ std::string fstab_buf = ReadFstabFromDt();
if (fstab_buf.empty()) {
- LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
- return nullptr;
+ if (log) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
+ return false;
}
std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
fstab_buf.length(), "r"), fclose);
if (!fstab_file) {
- PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
- return nullptr;
+ if (log) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+ return false;
}
- struct fstab* fstab = fs_mgr_read_fstab_file(fstab_file.get(), false);
- if (!fstab) {
- LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:"
- << std::endl << fstab_buf;
- }
-
- return fstab;
-}
-
-/*
- * Identify path to fstab file. Lookup is based on pattern
- * fstab.<hardware>, fstab.<hardware.platform> in folders
- /odm/etc, vendor/etc, or /.
- */
-static std::string get_fstab_path()
-{
- for (const char* prop : {"hardware", "hardware.platform"}) {
- std::string hw;
-
- if (!fs_mgr_get_boot_config(prop, &hw)) continue;
-
- for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
- std::string fstab_path = prefix + hw;
- if (access(fstab_path.c_str(), F_OK) == 0) {
- return fstab_path;
- }
+ if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
+ if (log) {
+ LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
+ << fstab_buf;
}
+ return false;
}
- return std::string();
+ SkipMountingPartitions(fstab);
+
+ return true;
}
-/*
- * loads the fstab file and combines with fstab entries passed in from device tree.
- */
-struct fstab *fs_mgr_read_fstab_default()
-{
- std::string default_fstab;
+// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
+// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
+// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
+// GSI will be used.
+bool SkipMountingPartitions(Fstab* fstab) {
+ constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
+ std::string skip_config;
+ auto save_errno = errno;
+ if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
+ errno = save_errno; // missing file is expected
+ return true;
+ }
+
+ for (const auto& skip_mount_point : Split(skip_config, "\n")) {
+ if (skip_mount_point.empty()) {
+ continue;
+ }
+ auto it = std::remove_if(fstab->begin(), fstab->end(),
+ [&skip_mount_point](const auto& entry) {
+ return entry.mount_point == skip_mount_point;
+ });
+ if (it == fstab->end()) continue;
+ fstab->erase(it, fstab->end());
+ LOG(INFO) << "Skip mounting partition: " << skip_mount_point;
+ }
+
+ return true;
+}
+
+// Loads the fstab file and combines with fstab entries passed in from device tree.
+bool ReadDefaultFstab(Fstab* fstab) {
+ Fstab dt_fstab;
+ ReadFstabFromDt(&dt_fstab, false);
+
+ *fstab = std::move(dt_fstab);
+
+ std::string default_fstab_path;
// Use different fstab paths for normal boot and recovery boot, respectively
if (access("/system/bin/recovery", F_OK) == 0) {
- default_fstab = "/etc/recovery.fstab";
+ default_fstab_path = "/etc/recovery.fstab";
} else { // normal boot
- default_fstab = get_fstab_path();
+ default_fstab_path = GetFstabPath();
}
- struct fstab* fstab = nullptr;
- if (!default_fstab.empty()) {
- fstab = fs_mgr_read_fstab(default_fstab.c_str());
+ Fstab default_fstab;
+ if (!default_fstab_path.empty()) {
+ ReadFstabFromFile(default_fstab_path, &default_fstab);
} else {
LINFO << __FUNCTION__ << "(): failed to find device default fstab";
}
- struct fstab* fstab_dt = fs_mgr_read_fstab_dt();
-
- // combines fstab entries passed in from device tree with
- // the ones found from default_fstab file
- return in_place_merge(fstab_dt, fstab);
-}
-
-void fs_mgr_free_fstab(struct fstab *fstab)
-{
- int i;
-
- if (!fstab) {
- return;
+ for (auto&& entry : default_fstab) {
+ fstab->emplace_back(std::move(entry));
}
- for (i = 0; i < fstab->num_entries; i++) {
- /* Free the pointers return by strdup(3) */
- free(fstab->recs[i].blk_device);
- free(fstab->recs[i].logical_partition_name);
- free(fstab->recs[i].mount_point);
- free(fstab->recs[i].fs_type);
- free(fstab->recs[i].fs_options);
- free(fstab->recs[i].key_loc);
- free(fstab->recs[i].key_dir);
- free(fstab->recs[i].label);
- free(fstab->recs[i].sysfs_path);
- }
-
- /* Free the fstab_recs array created by calloc(3) */
- free(fstab->recs);
-
- /* Free fstab */
- free(fstab);
+ return !fstab->empty();
}
-/* Add an entry to the fstab, and return 0 on success or -1 on error */
-int fs_mgr_add_entry(struct fstab *fstab,
- const char *mount_point, const char *fs_type,
- const char *blk_device)
-{
- struct fstab_rec *new_fstab_recs;
- int n = fstab->num_entries;
-
- new_fstab_recs = (struct fstab_rec *)
- realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1));
-
- if (!new_fstab_recs) {
- return -1;
- }
-
- /* A new entry was added, so initialize it */
- memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec));
- new_fstab_recs[n].mount_point = strdup(mount_point);
- new_fstab_recs[n].fs_type = strdup(fs_type);
- new_fstab_recs[n].blk_device = strdup(blk_device);
- new_fstab_recs[n].length = 0;
-
- /* Update the fstab struct */
- fstab->recs = new_fstab_recs;
- fstab->num_entries++;
-
- return 0;
-}
-
-/*
- * Returns the fstab_rec* whose mount_point is path.
- * Returns nullptr if not found.
- */
-struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
- if (!fstab) {
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
+ if (fstab == nullptr) {
return nullptr;
}
- for (int i = 0; i < fstab->num_entries; i++) {
- if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) {
- return &fstab->recs[i];
+
+ for (auto& entry : *fstab) {
+ if (entry.mount_point == path) {
+ return &entry;
}
}
+
return nullptr;
}
-std::set<std::string> fs_mgr_get_boot_devices() {
- // boot_devices can be specified in device tree.
- std::string dt_value;
- std::string file_name = get_android_dt_dir() + "/boot_devices";
- if (read_dt_file(file_name, &dt_value)) {
- auto boot_devices = android::base::Split(dt_value, ",");
+std::set<std::string> GetBootDevices() {
+ // First check the kernel commandline, then try the device tree otherwise
+ std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
+ std::string value;
+ if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
+ ReadDtFile(dt_file_name, &value)) {
+ auto boot_devices = Split(value, ",");
return std::set<std::string>(boot_devices.begin(), boot_devices.end());
}
// Fallback to extract boot devices from fstab.
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (fstab) return extract_boot_devices(*fstab);
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return {};
+ }
- return {};
+ return ExtraBootDevices(fstab);
}
-int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_VOLDMANAGED;
+FstabEntry BuildGsiSystemFstabEntry() {
+ // .logical_partition_name is required to look up AVB Hashtree descriptors.
+ FstabEntry system = {
+ .blk_device = "system_gsi",
+ .mount_point = "/system",
+ .fs_type = "ext4",
+ .flags = MS_RDONLY,
+ .fs_options = "barrier=1",
+ // could add more keys separated by ':'.
+ .avb_keys = "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey",
+ .logical_partition_name = "system"};
+ system.fs_mgr_flags.wait = true;
+ system.fs_mgr_flags.logical = true;
+ system.fs_mgr_flags.first_stage_mount = true;
+ return system;
}
-int fs_mgr_is_nonremovable(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_NONREMOVABLE;
+std::string GetVerityDeviceName(const FstabEntry& entry) {
+ std::string base_device;
+ if (entry.mount_point == "/") {
+ // When using system-as-root, the device name is fixed as "vroot".
+ if (entry.fs_mgr_flags.avb) {
+ return "vroot";
+ }
+ base_device = "system";
+ } else {
+ base_device = android::base::Basename(entry.mount_point);
+ }
+ return base_device + "-verity";
}
-int fs_mgr_is_verified(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_VERIFY;
+} // namespace fs_mgr
+} // namespace android
+
+// FIXME: The same logic is duplicated in system/core/init/
+const std::string& get_android_dt_dir() {
+ // Set once and saves time for subsequent calls to this function
+ static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
+ return kAndroidDtDir;
}
-int fs_mgr_is_avb(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_AVB;
-}
+bool is_dt_compatible() {
+ std::string file_name = get_android_dt_dir() + "/compatible";
+ std::string dt_value;
+ if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
+ if (dt_value == "android,firmware") {
+ return true;
+ }
+ }
-int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_VERIFYATBOOT;
-}
-
-int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
-}
-
-int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
-}
-
-void fs_mgr_get_file_encryption_modes(const struct fstab_rec *fstab,
- const char **contents_mode_ret,
- const char **filenames_mode_ret)
-{
- *contents_mode_ret = flag_to_encryption_mode(file_contents_encryption_modes,
- fstab->file_contents_mode);
- *filenames_mode_ret = flag_to_encryption_mode(file_names_encryption_modes,
- fstab->file_names_mode);
-}
-
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
-}
-
-int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
-}
-
-int fs_mgr_is_notrim(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_NOTRIM;
-}
-
-int fs_mgr_is_formattable(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & (MF_FORMATTABLE);
-}
-
-int fs_mgr_is_slotselect(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_SLOTSELECT;
-}
-
-int fs_mgr_is_nofail(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_NOFAIL;
-}
-
-int fs_mgr_is_latemount(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_LATEMOUNT;
-}
-
-int fs_mgr_is_quota(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_QUOTA;
-}
-
-int fs_mgr_has_sysfs_path(const struct fstab_rec *fstab)
-{
- return fstab->fs_mgr_flags & MF_SYSFS;
-}
-
-int fs_mgr_is_logical(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_LOGICAL;
-}
-
-int fs_mgr_is_checkpoint(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & (MF_CHECKPOINT_FS | MF_CHECKPOINT_BLK);
-}
-
-int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_CHECKPOINT_FS;
-}
-
-int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
- return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
+ return false;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 79957f6..ac15ce4 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -20,17 +20,18 @@
#include <linux/fs.h>
#include <selinux/selinux.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <algorithm>
-#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -42,10 +43,12 @@
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
+#include <fs_mgr/file_wait.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
@@ -55,17 +58,39 @@
using namespace android::dm;
using namespace android::fs_mgr;
-#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+namespace {
-bool fs_mgr_overlayfs_mount_all(fstab*) {
- return false;
+bool fs_mgr_access(const std::string& path) {
+ auto save_errno = errno;
+ auto ret = access(path.c_str(), F_OK) == 0;
+ errno = save_errno;
+ return ret;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab*) {
+// determine if a filesystem is available
+bool fs_mgr_overlayfs_filesystem_available(const std::string& filesystem) {
+ std::string filesystems;
+ if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
+ return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
+}
+
+} // namespace
+
+#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
return {};
}
-bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change) {
+bool fs_mgr_overlayfs_mount_all(Fstab*) {
+ return false;
+}
+
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
+ return {};
+}
+
+bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
if (change) *change = false;
return false;
}
@@ -75,6 +100,10 @@
return false;
}
+bool fs_mgr_overlayfs_is_setup() {
+ return false;
+}
+
#else // ALLOW_ADBD_DISABLE_VERITY == 0
namespace {
@@ -104,41 +133,45 @@
return ret | !rmdir(test_directory.c_str());
}
-std::string fs_mgr_get_context(const std::string& mount_point) {
- char* ctx = nullptr;
- auto len = getfilecon(mount_point.c_str(), &ctx);
- if ((len > 0) && ctx) {
- std::string context(ctx, len);
- free(ctx);
- return context;
- }
- return "";
-}
-
// At less than 1% free space return value of false,
// means we will try to wrap with overlayfs.
-bool fs_mgr_filesystem_has_space(const char* mount_point) {
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
// If we have access issues to find out space remaining, return true
// to prevent us trying to override with overlayfs.
struct statvfs vst;
- if (statvfs(mount_point, &vst)) return true;
+ auto save_errno = errno;
+ if (statvfs(mount_point.c_str(), &vst)) {
+ errno = save_errno;
+ return true;
+ }
static constexpr int kPercentThreshold = 1; // 1%
return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
}
-bool fs_mgr_overlayfs_enabled(struct fstab_rec* fsrec) {
+bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
// readonly filesystem, can not be mount -o remount,rw
- // if squashfs or if free space is (near) zero making such a remount
+ // for squashfs, erofs or if free space is (near) zero making such a remount
// virtually useless, or if there are shared blocks that prevent remount,rw
- if (("squashfs"s == fsrec->fs_type) || !fs_mgr_filesystem_has_space(fsrec->mount_point)) {
+ if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
return true;
}
- if (fs_mgr_is_logical(fsrec)) {
- fs_mgr_update_logical_partition(fsrec);
+ if (entry->fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(entry);
}
- return fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device);
+ auto save_errno = errno;
+ errno = 0;
+ auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
+ if (!has_shared_blocks && (entry->mount_point == "/system")) {
+ has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
+ }
+ // special case for first stage init for system as root (taimen)
+ if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
+ has_shared_blocks = true;
+ }
+ errno = save_errno;
+ return has_shared_blocks;
}
bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
@@ -213,24 +246,19 @@
std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
if (candidate.empty()) return "";
-
- return "override_creds=off," + kLowerdirOption + mount_point + "," + kUpperdirOption +
- candidate + kUpperName + ",workdir=" + candidate + kWorkName;
+ auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
+ ",workdir=" + candidate + kWorkName;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+ ret += ",override_creds=off";
+ }
+ return ret;
}
-const char* fs_mgr_mount_point(const char* mount_point) {
- if (!mount_point) return mount_point;
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
if ("/"s != mount_point) return mount_point;
return "/system";
}
-bool fs_mgr_access(const std::string& path) {
- auto save_errno = errno;
- auto ret = access(path.c_str(), F_OK) == 0;
- errno = save_errno;
- return ret;
-}
-
bool fs_mgr_rw_access(const std::string& path) {
if (path.empty()) return false;
auto save_errno = errno;
@@ -239,36 +267,19 @@
return ret;
}
-// return true if system supports overlayfs
-bool fs_mgr_wants_overlayfs() {
- // Properties will return empty on init first_stage_mount, so speculative
- // determination, empty (unset) _or_ "1" is true which differs from the
- // official ro.debuggable policy. ALLOW_ADBD_DISABLE_VERITY == 0 should
- // protect us from false in any case, so this is insurance.
- auto debuggable = android::base::GetProperty("ro.debuggable", "1");
- if (debuggable != "1") return false;
-
- // Overlayfs available in the kernel, and patched for override_creds?
- return fs_mgr_access("/sys/module/overlay/parameters/override_creds");
-}
-
bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
- fs_mgr_free_fstab);
- if (!fstab) return false;
+ Fstab fstab;
+ auto save_errno = errno;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return false;
+ }
+ errno = save_errno;
const auto lowerdir = kLowerdirOption + mount_point;
- for (auto i = 0; i < fstab->num_entries; ++i) {
- const auto fsrec = &fstab->recs[i];
- const auto fs_type = fsrec->fs_type;
- if (!fs_type) continue;
- if (overlay_only && ("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if (mount_point != fsrec_mount_point) continue;
+ for (const auto& entry : fstab) {
+ if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
+ if (mount_point != entry.mount_point) continue;
if (!overlay_only) return true;
- const auto fs_options = fsrec->fs_options;
- if (!fs_options) continue;
- const auto options = android::base::Split(fs_options, ",");
+ const auto options = android::base::Split(entry.fs_options, ",");
for (const auto& opt : options) {
if (opt == lowerdir) {
return true;
@@ -278,36 +289,23 @@
return false;
}
-std::vector<std::string> fs_mgr_overlayfs_verity_enabled_list() {
- std::vector<std::string> ret;
- fs_mgr_update_verity_state([&ret](fstab_rec*, const char* mount_point, int, int) {
- ret.emplace_back(mount_point);
- });
- return ret;
-}
-
-bool fs_mgr_wants_overlayfs(fstab_rec* fsrec) {
- if (!fsrec) return false;
-
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
- if (!fsrec->blk_device) return false;
-
- if (!fsrec->fs_type) return false;
-
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
// Don't check entries that are managed by vold.
- if (fsrec->fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) return false;
+ if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+ // *_other doesn't want overlayfs.
+ if (entry->fs_mgr_flags.slot_select_other) return false;
// Only concerned with readonly partitions.
- if (!(fsrec->flags & MS_RDONLY)) return false;
+ if (!(entry->flags & MS_RDONLY)) return false;
// If unbindable, do not allow overlayfs as this could expose us to
// security issues. On Android, this could also be used to turn off
// the ability to overlay an otherwise acceptable filesystem since
// /system and /vendor are never bound(sic) to.
- if (fsrec->flags & MS_UNBINDABLE) return false;
+ if (entry->flags & MS_UNBINDABLE) return false;
- if (!fs_mgr_overlayfs_enabled(fsrec)) return false;
+ if (!fs_mgr_overlayfs_enabled(entry)) return false;
return true;
}
@@ -338,6 +336,7 @@
bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
bool* change) {
auto ret = true;
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) return ret;
auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
if (setfscreatecon(kOverlayfsFileContext)) {
@@ -389,19 +388,33 @@
return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
}
+const auto kPhysicalDevice = "/dev/block/by-name/"s;
+
std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
- return "/dev/block/by-name/" + fs_mgr_get_super_partition_name(slot_number);
+ return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
}
-bool fs_mgr_overlayfs_has_logical(const fstab* fstab) {
- if (!fstab) return false;
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto fsrec = &fstab->recs[i];
- if (fs_mgr_is_logical(fsrec)) return true;
+bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+ for (const auto& entry : fstab) {
+ if (entry.fs_mgr_flags.logical) {
+ return true;
+ }
}
return false;
}
+void fs_mgr_overlayfs_umount_scratch() {
+ // Lazy umount will allow us to move on and possibly later
+ // establish a new fresh mount without requiring a reboot should
+ // the developer wish to restart. Old references should melt
+ // away or have no data. Main goal is to shut the door on the
+ // current overrides with an expectation of a subsequent reboot,
+ // thus any errors here are ignored.
+ umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+ LINFO << "umount(" << kScratchMountPoint << ")";
+ rmdir(kScratchMountPoint.c_str());
+}
+
// reduce 'DM_DEV_STATUS failed for scratch: No such device or address' noise
std::string scratch_device_cache;
@@ -415,13 +428,7 @@
auto save_errno = errno;
if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
- // Lazy umount will allow us to move on and possibly later
- // establish a new fresh mount without requiring a reboot should
- // the developer wish to restart. Old references should melt
- // away or have no data. Main goal is to shut the door on the
- // current overrides with an expectation of a subsequent reboot,
- // thus any errors here are ignored.
- umount2(kScratchMountPoint.c_str(), MNT_DETACH);
+ fs_mgr_overlayfs_umount_scratch();
}
auto builder = MetadataBuilder::New(super_device, slot_number);
if (!builder) {
@@ -437,9 +444,9 @@
auto metadata = builder->Export();
if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
if (change) *change = true;
- if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+ if (!DestroyLogicalPartition(partition_name)) return false;
} else {
- PERROR << "delete partition " << overlay;
+ LERROR << "delete partition " << overlay;
return false;
}
errno = save_errno;
@@ -510,14 +517,170 @@
return ret;
}
+bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+ auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+ nullptr);
+ if (ret) {
+ PERROR << "__mount(target=" << mount_point
+ << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+ auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+ if (ret) {
+ PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+ return false;
+ }
+ return true;
+}
+
+struct mount_info {
+ std::string mount_point;
+ bool shared_flag;
+};
+
+std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+ std::vector<mount_info> info;
+
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+ return info;
+ }
+
+ ssize_t len;
+ size_t alloc_len = 0;
+ char* line = nullptr;
+ while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ static constexpr char delim[] = " \t";
+ char* save_ptr;
+ if (!strtok_r(line, delim, &save_ptr)) {
+ LERROR << "Error parsing mount ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing parent ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount source";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing root";
+ break;
+ }
+
+ char* p;
+ if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_point";
+ break;
+ }
+ mount_info entry = {p, false};
+
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount_flags";
+ break;
+ }
+
+ while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+ if ((p[0] == '-') && (p[1] == '\0')) break;
+ if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+ }
+ if (!p) {
+ LERROR << "Error parsing fields";
+ break;
+ }
+ info.emplace_back(std::move(entry));
+ }
+
+ free(line);
+ if (info.empty()) {
+ LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+ }
+ return info;
+}
+
bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
+ auto retval = true;
+ auto save_errno = errno;
+
+ struct move_entry {
+ std::string mount_point;
+ std::string dir;
+ bool shared_flag;
+ };
+ std::vector<move_entry> move;
+ auto parent_private = false;
+ auto parent_made_private = false;
+ auto dev_private = false;
+ auto dev_made_private = false;
+ for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+ if ((entry.mount_point == mount_point) && !entry.shared_flag) {
+ parent_private = true;
+ }
+ if ((entry.mount_point == "/dev") && !entry.shared_flag) {
+ dev_private = true;
+ }
+
+ if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+ continue;
+ }
+ if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
+ return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+ }) != move.end()) {
+ continue;
+ }
+
+ // use as the bound directory in /dev.
+ auto new_context = fs_mgr_get_context(entry.mount_point);
+ if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+ PERROR << "setfscreatecon " << new_context;
+ }
+ move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
+ entry.shared_flag};
+ const auto target = mkdtemp(new_entry.dir.data());
+ if (!target) {
+ retval = false;
+ save_errno = errno;
+ PERROR << "temporary directory for MS_BIND";
+ setfscreatecon(nullptr);
+ continue;
+ }
+ setfscreatecon(nullptr);
+
+ if (!parent_private && !parent_made_private) {
+ parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ }
+ if (new_entry.shared_flag) {
+ new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+ }
+ if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+ retval = false;
+ save_errno = errno;
+ if (new_entry.shared_flag) {
+ fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+ }
+ continue;
+ }
+ move.emplace_back(std::move(new_entry));
+ }
+
// hijack __mount() report format to help triage
auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
const auto opt_list = android::base::Split(options, ",");
- for (const auto opt : opt_list) {
+ for (const auto& opt : opt_list) {
if (android::base::StartsWith(opt, kUpperdirOption)) {
report = report + "," + opt;
break;
@@ -528,72 +691,53 @@
auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
options.c_str());
if (ret) {
+ retval = false;
+ save_errno = errno;
PERROR << report << ret;
- return false;
} else {
LINFO << report << ret;
- return true;
}
-}
-std::vector<std::string> fs_mgr_candidate_list(fstab* fstab, const char* mount_point = nullptr) {
- std::vector<std::string> mounts;
- if (!fstab) return mounts;
-
- auto verity = fs_mgr_overlayfs_verity_enabled_list();
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto fsrec = &fstab->recs[i];
- if (!fs_mgr_wants_overlayfs(fsrec)) continue;
- std::string new_mount_point(fs_mgr_mount_point(fsrec->mount_point));
- if (mount_point && (new_mount_point != mount_point)) continue;
- if (std::find(verity.begin(), verity.end(), android::base::Basename(new_mount_point)) !=
- verity.end()) {
- continue;
+ // Move submounts back.
+ for (const auto& entry : move) {
+ if (!dev_private && !dev_made_private) {
+ dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
}
- auto duplicate_or_more_specific = false;
- for (auto it = mounts.begin(); it != mounts.end();) {
- if ((*it == new_mount_point) ||
- (android::base::StartsWith(new_mount_point, *it + "/"))) {
- duplicate_or_more_specific = true;
- break;
- }
- if (android::base::StartsWith(*it, new_mount_point + "/")) {
- it = mounts.erase(it);
- } else {
- ++it;
- }
+
+ if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+ retval = false;
+ save_errno = errno;
+ } else if (entry.shared_flag &&
+ !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+ retval = false;
+ save_errno = errno;
}
- if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
+ rmdir(entry.dir.c_str());
+ }
+ if (dev_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/dev", true);
+ }
+ if (parent_made_private) {
+ fs_mgr_overlayfs_set_shared_mount(mount_point, true);
}
- // if not itemized /system or /, system as root, fake one up?
-
- // do we want or need to?
- if (mount_point && ("/system"s != mount_point)) return mounts;
- if (std::find(mounts.begin(), mounts.end(), "/system") != mounts.end()) return mounts;
-
- // fs_mgr_overlayfs_verity_enabled_list says not to?
- if (std::find(verity.begin(), verity.end(), "system") != verity.end()) return mounts;
-
- // confirm that fstab is missing system
- if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/")) {
- return mounts;
- }
- if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), "/system")) {
- return mounts;
- }
-
- // We have a stunted fstab (w/o system or / ) passed in by the caller,
- // verity claims are assumed accurate because they are collected internally
- // from fs_mgr_fstab_default() from within fs_mgr_update_verity_state(),
- // Can (re)evaluate /system with impunity since we know it is ever-present.
- mounts.emplace_back("/system");
- return mounts;
+ errno = save_errno;
+ return retval;
}
// Mount kScratchMountPoint
-bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type) {
- if (!fs_mgr_rw_access(device_path)) return false;
+bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
+ bool readonly = false) {
+ if (readonly) {
+ if (!fs_mgr_access(device_path)) return false;
+ } else {
+ if (!fs_mgr_rw_access(device_path)) return false;
+ }
+
+ auto f2fs = fs_mgr_is_f2fs(device_path);
+ auto ext4 = fs_mgr_is_ext4(device_path);
+ if (!f2fs && !ext4) return false;
+
if (setfscreatecon(kOverlayfsFileContext)) {
PERROR << "setfscreatecon " << kOverlayfsFileContext;
}
@@ -601,26 +745,28 @@
PERROR << "create " << kScratchMountPoint;
}
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> local_fstab(
- static_cast<fstab*>(calloc(1, sizeof(fstab))), fs_mgr_free_fstab);
- auto fsrec = static_cast<fstab_rec*>(calloc(1, sizeof(fstab_rec)));
- local_fstab->num_entries = 1;
- local_fstab->recs = fsrec;
- fsrec->blk_device = strdup(device_path.c_str());
- fsrec->mount_point = strdup(kScratchMountPoint.c_str());
- fsrec->fs_type = strdup(mnt_type.c_str());
- fsrec->flags = MS_RELATIME;
- fsrec->fs_options = strdup("");
+ FstabEntry entry;
+ entry.blk_device = device_path;
+ entry.mount_point = kScratchMountPoint;
+ entry.fs_type = mnt_type;
+ if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
+ if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
+ entry.flags = MS_RELATIME;
+ if (readonly) {
+ entry.flags |= MS_RDONLY;
+ } else {
+ fs_mgr_set_blk_ro(device_path, false);
+ }
auto save_errno = errno;
- auto mounted = fs_mgr_do_mount_one(fsrec) == 0;
+ auto mounted = fs_mgr_do_mount_one(entry) == 0;
if (!mounted) {
- free(fsrec->fs_type);
- if (mnt_type == "f2fs") {
- fsrec->fs_type = strdup("ext4");
- } else {
- fsrec->fs_type = strdup("f2fs");
+ if ((entry.fs_type == "f2fs") && ext4) {
+ entry.fs_type = "ext4";
+ mounted = fs_mgr_do_mount_one(entry) == 0;
+ } else if ((entry.fs_type == "ext4") && f2fs) {
+ entry.fs_type = "f2fs";
+ mounted = fs_mgr_do_mount_one(entry) == 0;
}
- mounted = fs_mgr_do_mount_one(fsrec) == 0;
if (!mounted) save_errno = errno;
}
setfscreatecon(nullptr);
@@ -634,89 +780,199 @@
// Only a suggestion for _first_ try during mounting
std::string fs_mgr_overlayfs_scratch_mount_type() {
- if (!access(kMkF2fs.c_str(), X_OK)) return "f2fs";
- if (!access(kMkExt4.c_str(), X_OK)) return "ext4";
+ if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("f2fs")) {
+ return "f2fs";
+ }
+ if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("ext4")) {
+ return "ext4";
+ }
return "auto";
}
std::string fs_mgr_overlayfs_scratch_device() {
if (!scratch_device_cache.empty()) return scratch_device_cache;
- auto& dm = DeviceMapper::Instance();
- const auto partition_name = android::base::Basename(kScratchMountPoint);
- std::string path;
- if (!dm.GetDmDevicePathByName(partition_name, &path)) return "";
+ // Is this a multiple super device (retrofit)?
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
+ if (super_device == path) {
+ // Create from within single super device;
+ auto& dm = DeviceMapper::Instance();
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ if (!dm.GetDmDevicePathByName(partition_name, &path)) {
+ // non-DAP A/B device?
+ if (fs_mgr_access(super_device)) return "";
+ auto other_slot = fs_mgr_get_other_slot_suffix();
+ if (other_slot.empty()) return "";
+ path = kPhysicalDevice + "system" + other_slot;
+ }
+ }
return scratch_device_cache = path;
}
-// Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const fstab* fstab, bool* change) {
- if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
- auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
- auto scratch_device = fs_mgr_overlayfs_scratch_device();
- auto partition_exists = fs_mgr_rw_access(scratch_device);
- if (!partition_exists) {
- auto slot_number = fs_mgr_overlayfs_slot_number();
- auto super_device = fs_mgr_overlayfs_super_device(slot_number);
- if (!fs_mgr_rw_access(super_device)) return false;
- if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
- auto builder = MetadataBuilder::New(super_device, slot_number);
- if (!builder) {
- PERROR << "open " << super_device << " metadata";
+bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
+ // Force mkfs by design for overlay support of adb remount, simplify and
+ // thus do not rely on fsck to correct problems that could creep in.
+ auto command = ""s;
+ if (mnt_type == "f2fs") {
+ command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+ } else if (mnt_type == "ext4") {
+ command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+ } else {
+ errno = ESRCH;
+ LERROR << mnt_type << " has no mkfs cookbook";
+ return false;
+ }
+ command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
+ fs_mgr_set_blk_ro(scratch_device, false);
+ auto ret = system(command.c_str());
+ if (ret) {
+ LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
+ return false;
+ }
+ return true;
+}
+
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+ auto& dm = DeviceMapper::Instance();
+
+ // Remove <other> partitions
+ for (const auto& group : builder->ListGroups()) {
+ for (const auto& part : builder->ListPartitionsInGroup(group)) {
+ const auto& name = part->name();
+ if (!android::base::EndsWith(name, suffix)) {
+ continue;
+ }
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
+ continue;
+ }
+ builder->ResizePartition(builder->FindPartition(name), 0);
+ }
+ }
+}
+
+// This is where we find and steal backing storage from the system.
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+ bool* partition_exists, bool* change) {
+ *scratch_device = fs_mgr_overlayfs_scratch_device();
+ *partition_exists = fs_mgr_rw_access(*scratch_device);
+ auto partition_create = !*partition_exists;
+ // Do we need to create a logical "scratch" partition?
+ if (!partition_create && android::base::StartsWith(*scratch_device, kPhysicalDevice)) {
+ return true;
+ }
+ auto slot_number = fs_mgr_overlayfs_slot_number();
+ auto super_device = fs_mgr_overlayfs_super_device(slot_number);
+ if (!fs_mgr_rw_access(super_device)) return false;
+ if (!fs_mgr_overlayfs_has_logical(fstab)) return false;
+ auto builder = MetadataBuilder::New(super_device, slot_number);
+ if (!builder) {
+ LERROR << "open " << super_device << " metadata";
+ return false;
+ }
+ const auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto partition = builder->FindPartition(partition_name);
+ *partition_exists = partition != nullptr;
+ auto changed = false;
+ if (!*partition_exists) {
+ partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+ if (!partition) {
+ LERROR << "create " << partition_name;
return false;
}
- const auto partition_name = android::base::Basename(kScratchMountPoint);
- partition_exists = builder->FindPartition(partition_name) != nullptr;
- if (!partition_exists) {
- auto partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
- if (!partition) {
- PERROR << "create " << partition_name;
- return false;
+ changed = true;
+ }
+ // Take half of free space, minimum 512MB or maximum free - margin.
+ static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+ if (partition->size() < kMinimumSize) {
+ auto partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ if ((partition_size > kMinimumSize) || !partition->size()) {
+ // Leave some space for free space jitter of a few erase
+ // blocks, in case they are needed for any individual updates
+ // to any other partition that needs to be flashed while
+ // overlayfs is in force. Of course if margin_size is not
+ // enough could normally get a flash failure, so
+ // ResizePartition() will delete the scratch partition in
+ // order to fulfill. Deleting scratch will destroy all of
+ // the adb remount overrides :-( .
+ auto margin_size = uint64_t(3 * 256 * 1024);
+ BlockDeviceInfo info;
+ if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
+ margin_size = 3 * info.logical_block_size;
}
- auto partition_size = builder->AllocatableSpace() - builder->UsedSpace();
- // 512MB or half the remaining available space, whichever is greater.
- partition_size = std::max(uint64_t(512 * 1024 * 1024), partition_size / 2);
- if (!builder->ResizePartition(partition, partition_size)) {
- PERROR << "resize " << partition_name;
- return false;
- }
+ partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+ partition_size / 2);
+ if (partition_size > partition->size()) {
+ if (!builder->ResizePartition(partition, partition_size)) {
+ // Try to free up space by deallocating partitions in the other slot.
+ TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
- auto metadata = builder->Export();
- if (!metadata) {
- LERROR << "generate new metadata " << partition_name;
- return false;
+ partition_size =
+ builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+ partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
+ partition_size / 2);
+ if (!builder->ResizePartition(partition, partition_size)) {
+ LERROR << "resize " << partition_name;
+ return false;
+ }
+ }
+ if (!partition_create) DestroyLogicalPartition(partition_name);
+ changed = true;
+ *partition_exists = false;
}
- if (!UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
- LERROR << "update " << partition_name;
- return false;
- }
-
- if (change) *change = true;
+ }
+ }
+ // land the update back on to the partition
+ if (changed) {
+ auto metadata = builder->Export();
+ if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+ LERROR << "add partition " << partition_name;
+ return false;
}
- if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
- &scratch_device))
- return false;
+ if (change) *change = true;
}
+ if (changed || partition_create) {
+ if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
+ scratch_device))
+ return false;
+
+ if (change) *change = true;
+ }
+ return true;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab, bool* change) {
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+
+ std::string scratch_device;
+ bool partition_exists;
+ if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists, change)) {
+ return false;
+ }
+
+ // If the partition exists, assume first that it can be mounted.
+ auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
if (partition_exists) {
if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
- if (change) *change = true;
- return true;
+ if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
+ !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+ // declare it useless, no overrides and no free space
+ fs_mgr_overlayfs_umount_scratch();
+ } else {
+ if (change) *change = true;
+ return true;
+ }
}
- // partition existed, but was not initialized;
+ // partition existed, but was not initialized; fall through to make it.
errno = 0;
}
- auto ret = system((mnt_type == "f2fs")
- ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
- : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
- " -O has_journal " + scratch_device)
- .c_str()));
- if (ret) {
- LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
- return false;
- }
+ if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
if (change) *change = true;
@@ -726,6 +982,7 @@
bool fs_mgr_overlayfs_scratch_can_be_mounted(const std::string& scratch_device) {
if (scratch_device.empty()) return false;
if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return false;
+ if (android::base::StartsWith(scratch_device, kPhysicalDevice)) return true;
if (fs_mgr_rw_access(scratch_device)) return true;
auto slot_number = fs_mgr_overlayfs_slot_number();
auto super_device = fs_mgr_overlayfs_super_device(slot_number);
@@ -735,28 +992,73 @@
return builder->FindPartition(android::base::Basename(kScratchMountPoint)) != nullptr;
}
+bool fs_mgr_overlayfs_invalid() {
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
+
+ // in recovery, fastbootd, or gsi mode, not allowed!
+ if (fs_mgr_access("/system/bin/recovery")) return true;
+ auto save_errno = errno;
+ auto ret = android::gsi::IsGsiRunning();
+ errno = save_errno;
+ return ret;
+}
+
} // namespace
-bool fs_mgr_overlayfs_mount_all(fstab* fstab) {
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ Fstab candidates;
+ for (const auto& entry : fstab) {
+ FstabEntry new_entry = entry;
+ if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+ !fs_mgr_wants_overlayfs(&new_entry)) {
+ continue;
+ }
+ auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+ auto duplicate_or_more_specific = false;
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+ if ((it_mount_point == new_mount_point) ||
+ (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+ duplicate_or_more_specific = true;
+ break;
+ }
+ if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+ it = candidates.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+ }
+ return candidates;
+}
+
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
auto ret = false;
-
- if (!fs_mgr_wants_overlayfs()) return ret;
-
- if (!fstab) return ret;
+ if (fs_mgr_overlayfs_invalid()) return ret;
auto scratch_can_be_mounted = true;
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ auto mount_point = fs_mgr_mount_point(entry.mount_point);
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+ ret = true;
+ continue;
+ }
if (scratch_can_be_mounted) {
scratch_can_be_mounted = false;
auto scratch_device = fs_mgr_overlayfs_scratch_device();
if (fs_mgr_overlayfs_scratch_can_be_mounted(scratch_device) &&
- fs_mgr_wait_for_file(scratch_device, 10s) &&
- fs_mgr_overlayfs_mount_scratch(scratch_device,
- fs_mgr_overlayfs_scratch_mount_type()) &&
- !fs_mgr_access(kScratchMountPoint + kOverlayTopDir)) {
- umount2(kScratchMountPoint.c_str(), MNT_DETACH);
- rmdir(kScratchMountPoint.c_str());
+ WaitForFile(scratch_device, 10s)) {
+ const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
+ if (fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type,
+ true /* readonly */)) {
+ auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
+ fs_mgr_overlayfs_umount_scratch();
+ if (has_overlayfs_dir) {
+ fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+ }
+ }
}
}
if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
@@ -764,13 +1066,16 @@
return ret;
}
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
- if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
+std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab* fstab) {
+ if (fs_mgr_overlayfs_invalid()) return {};
+
+ if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
return {};
}
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
auto device = fs_mgr_overlayfs_scratch_device();
if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
return {device};
@@ -780,46 +1085,65 @@
// Returns false if setup not permitted, errno set to last error.
// If something is altered, set *change.
-bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change) {
+bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
+ bool force) {
if (change) *change = false;
auto ret = false;
- if (!fs_mgr_wants_overlayfs()) return ret;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
if (!fs_mgr_boot_completed()) {
errno = EBUSY;
PERROR << "setup";
return ret;
}
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (!fstab) return ret;
- auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(mount_point));
- if (mounts.empty()) return ret;
+ auto save_errno = errno;
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return false;
+ }
+ errno = save_errno;
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ if (mount_point &&
+ (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+ it = candidates.erase(it);
+ continue;
+ }
+ save_errno = errno;
+ auto verity_enabled = !force && fs_mgr_is_verity_enabled(*it);
+ if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+ if (verity_enabled) {
+ it = candidates.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (candidates.empty()) return ret;
std::string dir;
for (const auto& overlay_mount_point : kOverlayMountPoints) {
if (backing && backing[0] && (overlay_mount_point != backing)) continue;
if (overlay_mount_point == kScratchMountPoint) {
- if (!fs_mgr_rw_access(fs_mgr_overlayfs_super_device(fs_mgr_overlayfs_slot_number())) ||
- !fs_mgr_overlayfs_has_logical(fstab.get())) {
+ if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
+ } else {
+ if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
continue;
}
- if (!fs_mgr_overlayfs_setup_scratch(fstab.get(), change)) continue;
- } else {
- if (!fs_mgr_get_entry_for_mount_point(fstab.get(), overlay_mount_point)) continue;
}
dir = overlay_mount_point;
break;
}
if (dir.empty()) {
- errno = ESRCH;
+ if (change && *change) errno = ESRCH;
+ if (errno == EPERM) errno = save_errno;
return ret;
}
std::string overlay;
ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
- for (const auto& fsrec_mount_point : mounts) {
- ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
+ for (const auto& entry : candidates) {
+ ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
}
return ret;
}
@@ -828,25 +1152,27 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- mount_point = fs_mgr_mount_point(mount_point);
auto ret = true;
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
+ auto mount_scratch = false;
if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
auto scratch_device = fs_mgr_overlayfs_scratch_device();
if (scratch_device.empty()) {
auto slot_number = fs_mgr_overlayfs_slot_number();
auto super_device = fs_mgr_overlayfs_super_device(slot_number);
const auto partition_name = android::base::Basename(kScratchMountPoint);
- CreateLogicalPartition(super_device, slot_number, partition_name, true, 0s,
+ CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
&scratch_device);
}
- fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type());
+ mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
+ fs_mgr_overlayfs_scratch_mount_type());
}
for (const auto& overlay_mount_point : kOverlayMountPoints) {
- ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
+ ret &= fs_mgr_overlayfs_teardown_one(
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
}
- if (!fs_mgr_wants_overlayfs()) {
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
// we didn't want overlayfs in the the first place, we do not want to
// waste time on a reboot (or reboot request message).
@@ -859,9 +1185,25 @@
PERROR << "teardown";
ret = false;
}
+ if (mount_scratch) fs_mgr_overlayfs_umount_scratch();
+
return ret;
}
+bool fs_mgr_overlayfs_is_setup() {
+ if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return false;
+ }
+ if (fs_mgr_overlayfs_invalid()) return false;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
+ }
+ return false;
+}
+
#endif // ALLOW_ADBD_DISABLE_VERITY != 0
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
@@ -885,3 +1227,42 @@
return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+ char* ctx = nullptr;
+ if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+ return "";
+ }
+
+ std::string context(ctx);
+ free(ctx);
+ return context;
+}
+
+OverlayfsValidResult fs_mgr_overlayfs_valid() {
+ // Overlayfs available in the kernel, and patched for override_creds?
+ if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
+ return OverlayfsValidResult::kOverrideCredsRequired;
+ }
+ if (!fs_mgr_overlayfs_filesystem_available("overlay")) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ struct utsname uts;
+ if (uname(&uts) == -1) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ int major, minor;
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (major < 4) {
+ return OverlayfsValidResult::kOk;
+ }
+ if (major > 4) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ if (minor > 3) {
+ return OverlayfsValidResult::kNotSupported;
+ }
+ return OverlayfsValidResult::kOk;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5e83cfb..c5e477c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#ifndef __CORE_FS_MGR_PRIV_H
-#define __CORE_FS_MGR_PRIV_H
+#pragma once
#include <chrono>
#include <string>
#include <android-base/logging.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
-#include "fs_mgr.h"
#include "fs_mgr_priv_boot_config.h"
/* The CHECK() in logging.h will use program invocation name as the tag.
@@ -39,11 +39,13 @@
#define LINFO LOG(INFO) << FS_MGR_TAG
#define LWARNING LOG(WARNING) << FS_MGR_TAG
#define LERROR LOG(ERROR) << FS_MGR_TAG
+#define LFATAL LOG(FATAL) << FS_MGR_TAG
// Logs a message with strerror(errno) at the end
#define PINFO PLOG(INFO) << FS_MGR_TAG
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
#define PERROR PLOG(ERROR) << FS_MGR_TAG
+#define PFATAL PLOG(FATAL) << FS_MGR_TAG
#define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
@@ -82,56 +84,23 @@
*
*/
-// clang-format off
-#define MF_WAIT 0x1
-#define MF_CHECK 0x2
-#define MF_CRYPT 0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED 0x10
-#define MF_LENGTH 0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO 0x80
-#define MF_ZRAMSIZE 0x100
-#define MF_VERIFY 0x200
-#define MF_FORCECRYPT 0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
- external storage */
-#define MF_NOTRIM 0x1000
-#define MF_FILEENCRYPTION 0x2000
-#define MF_FORMATTABLE 0x4000
-#define MF_SLOTSELECT 0x8000
-#define MF_FORCEFDEORFBE 0x10000
-#define MF_LATEMOUNT 0x20000
-#define MF_NOFAIL 0x40000
-#define MF_VERIFYATBOOT 0x80000
-#define MF_MAX_COMP_STREAMS 0x100000
-#define MF_RESERVEDSIZE 0x200000
-#define MF_QUOTA 0x400000
-#define MF_ERASEBLKSIZE 0x800000
-#define MF_LOGICALBLKSIZE 0X1000000
-#define MF_AVB 0X2000000
-#define MF_KEYDIRECTORY 0X4000000
-#define MF_SYSFS 0X8000000
-#define MF_LOGICAL 0x10000000
-#define MF_CHECKPOINT_BLK 0x20000000
-#define MF_CHECKPOINT_FS 0x40000000
-// clang-format on
-
#define DM_BUF_SIZE 4096
using namespace std::chrono_literals;
-enum class FileWaitMode { Exists, DoesNotExist };
-
-bool fs_mgr_wait_for_file(const std::string& filename,
- const std::chrono::milliseconds relative_timeout,
- FileWaitMode wait_mode = FileWaitMode::Exists);
-
-int fs_mgr_set_blk_ro(const char* blockdev);
-bool fs_mgr_update_for_slotselect(fstab* fstab);
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
+bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-int load_verity_state(fstab_rec* fstab, int* mode);
-#endif /* __CORE_FS_MGR_PRIV_H */
+bool fs_mgr_is_ext4(const std::string& blk_device);
+bool fs_mgr_is_f2fs(const std::string& blk_device);
+
+bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
+
+namespace android {
+namespace fs_mgr {
+bool UnmapDevice(const std::string& name);
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
new file mode 100644
index 0000000..149bee3
--- /dev/null
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libavb_user/libavb_user.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <fec/io.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_priv.h>
+#include <fstab/fstab.h>
+
+namespace {
+
+[[noreturn]] void usage(int exit_status) {
+ LOG(INFO) << getprogname()
+ << " [-h] [-R] [-T fstab_file] [partition]...\n"
+ "\t-h --help\tthis help\n"
+ "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
+ "\t-T --fstab\tcustom fstab file location\n"
+ "\tpartition\tspecific partition(s) (empty does all)\n"
+ "\n"
+ "Remount specified partition(s) read-write, by name or mount point.\n"
+ "-R notwithstanding, verity must be disabled on partition(s).";
+
+ ::exit(exit_status);
+}
+
+bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
+ if (entry.fs_mgr_flags.vold_managed) return false;
+ if (entry.fs_mgr_flags.recovery_only) return false;
+ if (entry.fs_mgr_flags.slot_select_other) return false;
+ if (!(entry.flags & MS_RDONLY)) return false;
+ if (entry.fs_type == "vfat") return false;
+ return true;
+}
+
+const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
+ if (entry.mount_point == "/") return "/system";
+ return entry.mount_point;
+}
+
+const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
+ const android::fs_mgr::FstabEntry& entry) {
+ auto mount_point = system_mount_point(entry);
+ auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
+ [&mount_point](const auto& entry) {
+ return android::base::StartsWith(mount_point,
+ system_mount_point(entry) + "/");
+ });
+ if (it == overlayfs_candidates.end()) return nullptr;
+ return &(*it);
+}
+
+void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ static const char log_characters[] = "VD\0WEFF";
+ if (severity < sizeof(log_characters)) {
+ auto severity_char = log_characters[severity];
+ if (severity_char) fprintf(stderr, "%c ", severity_char);
+ }
+ fprintf(stderr, "%s\n", message);
+
+ static auto logd = android::base::LogdLogger();
+ logd(id, severity, tag, file, line, message);
+}
+
+[[noreturn]] void reboot(bool overlayfs = false) {
+ if (overlayfs) {
+ LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
+ } else {
+ LOG(INFO) << "Successfully disabled verity\nrebooting device";
+ }
+ ::sync();
+ android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
+ ::sleep(60);
+ ::exit(0); // SUCCESS
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv, MyLogger);
+
+ enum {
+ SUCCESS,
+ NOT_USERDEBUG,
+ BADARG,
+ NOT_ROOT,
+ NO_FSTAB,
+ UNKNOWN_PARTITION,
+ INVALID_PARTITION,
+ VERITY_PARTITION,
+ BAD_OVERLAY,
+ NO_MOUNTS,
+ REMOUNT_FAILED,
+ } retval = SUCCESS;
+
+ // If somehow this executable is delivered on a "user" build, it can
+ // not function, so providing a clear message to the caller rather than
+ // letting if fall through and provide a lot of confusing failure messages.
+ if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
+ LOG(ERROR) << "only functions on userdebug or eng builds";
+ return NOT_USERDEBUG;
+ }
+
+ const char* fstab_file = nullptr;
+ auto can_reboot = false;
+
+ struct option longopts[] = {
+ {"fstab", required_argument, nullptr, 'T'},
+ {"help", no_argument, nullptr, 'h'},
+ {"reboot", no_argument, nullptr, 'R'},
+ {0, 0, nullptr, 0},
+ };
+ for (int opt; (opt = ::getopt_long(argc, argv, "hRT:", longopts, nullptr)) != -1;) {
+ switch (opt) {
+ case 'R':
+ can_reboot = true;
+ break;
+ case 'T':
+ if (fstab_file) {
+ LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
+ usage(BADARG);
+ }
+ fstab_file = optarg;
+ break;
+ default:
+ LOG(ERROR) << "Bad Argument -" << char(opt);
+ usage(BADARG);
+ break;
+ case 'h':
+ usage(SUCCESS);
+ break;
+ }
+ }
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "must be run as root";
+ return NOT_ROOT;
+ }
+
+ // Read the selected fstab.
+ android::fs_mgr::Fstab fstab;
+ auto fstab_read = false;
+ if (fstab_file) {
+ fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
+ } else {
+ fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
+ // Manufacture a / entry from /proc/mounts if missing.
+ if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
+ android::fs_mgr::Fstab mounts;
+ if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+ if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+ if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
+ }
+ }
+ }
+ }
+ if (!fstab_read || fstab.empty()) {
+ PLOG(ERROR) << "Failed to read fstab";
+ return NO_FSTAB;
+ }
+
+ // Generate the list of supported overlayfs mount points.
+ auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+ // Generate the all remountable partitions sub-list
+ android::fs_mgr::Fstab all;
+ for (auto const& entry : fstab) {
+ if (!remountable_partition(entry)) continue;
+ if (overlayfs_candidates.empty() ||
+ GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
+ (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
+ all.emplace_back(entry);
+ }
+ }
+
+ // Parse the unique list of valid partition arguments.
+ android::fs_mgr::Fstab partitions;
+ for (; argc > optind; ++optind) {
+ auto partition = std::string(argv[optind]);
+ if (partition.empty()) continue;
+ if (partition == "/") partition = "/system";
+ auto find_part = [&partition](const auto& entry) {
+ const auto mount_point = system_mount_point(entry);
+ if (partition == mount_point) return true;
+ if (partition == android::base::Basename(mount_point)) return true;
+ return false;
+ };
+ // Do we know about the partition?
+ auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
+ if (it == fstab.end()) {
+ LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
+ retval = UNKNOWN_PARTITION;
+ continue;
+ }
+ // Is that one covered by an existing overlayfs?
+ auto wrap = is_wrapped(overlayfs_candidates, *it);
+ if (wrap) {
+ LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
+ << wrap->mount_point << ", switching";
+ partition = system_mount_point(*wrap);
+ }
+ // Is it a remountable partition?
+ it = std::find_if(all.begin(), all.end(), find_part);
+ if (it == all.end()) {
+ LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
+ retval = INVALID_PARTITION;
+ continue;
+ }
+ if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
+ partitions.emplace_back(*it);
+ }
+ }
+
+ if (partitions.empty() && !retval) {
+ partitions = all;
+ }
+
+ // Check verity and optionally setup overlayfs backing.
+ auto reboot_later = false;
+ auto user_please_reboot_later = false;
+ auto setup_overlayfs = false;
+ auto just_disabled_verity = false;
+ for (auto it = partitions.begin(); it != partitions.end();) {
+ auto& entry = *it;
+ auto& mount_point = entry.mount_point;
+ if (fs_mgr_is_verity_enabled(entry)) {
+ retval = VERITY_PARTITION;
+ auto ret = false;
+ if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
+ if (AvbOps* ops = avb_ops_user_new()) {
+ ret = avb_user_verity_set(
+ ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
+ false);
+ avb_ops_user_free(ops);
+ }
+ if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
+ fec::io fh(entry.blk_device.c_str(), O_RDWR);
+ ret = fh && fh.set_verity_status(false);
+ }
+ if (ret) {
+ LOG(WARNING) << "Disabling verity for " << mount_point;
+ just_disabled_verity = true;
+ reboot_later = can_reboot;
+ user_please_reboot_later = true;
+ }
+ }
+ if (!ret) {
+ LOG(ERROR) << "Skipping " << mount_point << " for remount";
+ it = partitions.erase(it);
+ continue;
+ }
+ }
+
+ auto change = false;
+ errno = 0;
+ if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
+ if (change) {
+ LOG(INFO) << "Using overlayfs for " << mount_point;
+ reboot_later = can_reboot;
+ user_please_reboot_later = true;
+ setup_overlayfs = true;
+ }
+ } else if (errno) {
+ PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+ retval = BAD_OVERLAY;
+ it = partitions.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (partitions.empty() || just_disabled_verity) {
+ if (reboot_later) reboot(setup_overlayfs);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
+ LOG(WARNING) << "No partitions to remount";
+ return retval;
+ }
+
+ // Mount overlayfs.
+ errno = 0;
+ if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
+ retval = BAD_OVERLAY;
+ PLOG(ERROR) << "Can not mount overlayfs for partitions";
+ }
+
+ // Get actual mounts _after_ overlayfs has been added.
+ android::fs_mgr::Fstab mounts;
+ if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
+ PLOG(ERROR) << "Failed to read /proc/mounts";
+ retval = NO_MOUNTS;
+ }
+
+ // Remount selected partitions.
+ for (auto& entry : partitions) {
+ // unlock the r/o key for the mount point device
+ if (entry.fs_mgr_flags.logical) {
+ fs_mgr_update_logical_partition(&entry);
+ }
+ auto blk_device = entry.blk_device;
+ auto mount_point = entry.mount_point;
+
+ for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+ auto& rentry = *it;
+ if (mount_point == rentry.mount_point) {
+ blk_device = rentry.blk_device;
+ break;
+ }
+ // Find overlayfs mount point?
+ if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+ blk_device = rentry.blk_device;
+ mount_point = "/system";
+ break;
+ }
+ }
+ if (blk_device == "/dev/root") {
+ auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+ if (from_fstab) blk_device = from_fstab->blk_device;
+ }
+ fs_mgr_set_blk_ro(blk_device, false);
+
+ // Find system-as-root mount point?
+ if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+ GetEntryForMountPoint(&mounts, "/")) {
+ mount_point = "/";
+ }
+
+ // Now remount!
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
+ mount_point = entry.mount_point;
+ if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
+ nullptr) == 0) {
+ continue;
+ }
+ }
+ PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+ // If errno is EROFS at this point, we are dealing with r/o
+ // filesystem types like squashfs, erofs or ext4 dedupe. We will
+ // consider such a device that does not have CONFIG_OVERLAY_FS
+ // in the kernel as a misconfigured.
+ if (errno == EROFS) {
+ LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
+ }
+ retval = REMOUNT_FAILED;
+ }
+
+ if (reboot_later) reboot(setup_overlayfs);
+ if (user_please_reboot_later) {
+ LOG(INFO) << "Now reboot your device for settings to take effect";
+ return 0;
+ }
+
+ return retval;
+}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
new file mode 100644
index 0000000..1e65587
--- /dev/null
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_mgr/roots.h"
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+namespace android {
+namespace fs_mgr {
+
+static constexpr const char* kSystemRoot = "/system";
+
+static bool gDidMapLogicalPartitions = false;
+
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {
+ if (path.empty()) return nullptr;
+ std::string str(path);
+ while (true) {
+ auto entry = GetEntryForMountPoint(fstab, str);
+ if (entry != nullptr) return entry;
+ if (str == "/") break;
+ auto slash = str.find_last_of('/');
+ if (slash == std::string::npos) break;
+ if (slash == 0) {
+ str = "/";
+ } else {
+ str = str.substr(0, slash);
+ }
+ }
+ return nullptr;
+}
+
+enum class MountState {
+ ERROR = -1,
+ NOT_MOUNTED = 0,
+ MOUNTED = 1,
+};
+
+static MountState GetMountState(const std::string& mount_point) {
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ LERROR << "Failed to scan mounted volumes";
+ return MountState::ERROR;
+ }
+
+ auto mv = GetEntryForMountPoint(&mounted_fstab, mount_point);
+ if (mv != nullptr) {
+ return MountState::MOUNTED;
+ }
+ return MountState::NOT_MOUNTED;
+}
+
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_pt) {
+ auto rec = GetEntryForPath(fstab, path);
+ if (rec == nullptr) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+ if (rec->fs_type == "ramdisk") {
+ // The ramdisk is always mounted.
+ return true;
+ }
+
+ // If we can't acquire the block device for a logical partition, it likely
+ // was never created. In that case we try to create it.
+ if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {
+ if (gDidMapLogicalPartitions) {
+ LERROR << "Failed to find block device for partition";
+ return false;
+ }
+ std::string super_name = fs_mgr_get_super_partition_name();
+ if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+ LERROR << "Failed to create logical partitions";
+ return false;
+ }
+ gDidMapLogicalPartitions = true;
+ if (!fs_mgr_update_logical_partition(rec)) {
+ LERROR << "Failed to find block device for partition";
+ return false;
+ }
+ }
+
+ const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
+ auto mounted = GetMountState(mount_point);
+ if (mounted == MountState::ERROR) {
+ return false;
+ }
+ if (mounted == MountState::MOUNTED) {
+ return true;
+ }
+
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
+ if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
+ LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
+ return false;
+ }
+
+ int result = fs_mgr_do_mount_one(*rec, mount_point);
+ if (result == -1 && rec->fs_mgr_flags.formattable) {
+ PERROR << "Failed to mount " << mount_point << "; formatting";
+ bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+ if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
+ PERROR << "Failed to format " << mount_point;
+ return false;
+ }
+ result = fs_mgr_do_mount_one(*rec, mount_point);
+ }
+
+ if (result == -1) {
+ PERROR << "Failed to mount " << mount_point;
+ return false;
+ }
+ return true;
+}
+
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {
+ auto rec = GetEntryForPath(fstab, path);
+ if (rec == nullptr) {
+ LERROR << "unknown volume for path [" << path << "]";
+ return false;
+ }
+ if (rec->fs_type == "ramdisk") {
+ // The ramdisk is always mounted; you can't unmount it.
+ return false;
+ }
+
+ Fstab mounted_fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
+ LERROR << "Failed to scan mounted volumes";
+ return false;
+ }
+
+ auto mounted = GetMountState(rec->mount_point);
+ if (mounted == MountState::ERROR) {
+ return false;
+ }
+ if (mounted == MountState::NOT_MOUNTED) {
+ return true;
+ }
+
+ int result = umount(rec->mount_point.c_str());
+ if (result == -1) {
+ PWARNING << "Failed to umount " << rec->mount_point;
+ return false;
+ }
+ return true;
+}
+
+std::string GetSystemRoot() {
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ LERROR << "Failed to read default fstab";
+ return "";
+ }
+
+ auto entry = GetEntryForMountPoint(&fstab, kSystemRoot);
+ if (entry == nullptr) {
+ return "/";
+ }
+
+ return kSystemRoot;
+}
+
+bool LogicalPartitionsMapped() {
+ return gDidMapLogicalPartitions;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 3b01d0e..09c1b7e 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,6 +21,28 @@
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+// https://source.android.com/devices/tech/ota/ab/ab_implement#partitions
+// All partitions that are A/B-ed should be named as follows (slots are always
+// named a, b, etc.): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.
+static std::string other_suffix(const std::string& slot_suffix) {
+ if (slot_suffix == "_a") {
+ return "_b";
+ }
+ if (slot_suffix == "_b") {
+ return "_a";
+ }
+ return "";
+}
+
+// Returns "_b" or "_a", which is *the other* slot of androidboot.slot_suffix
+// in kernel cmdline, or an empty string if that parameter does not exist.
+std::string fs_mgr_get_other_slot_suffix() {
+ return other_suffix(fs_mgr_get_slot_suffix());
+}
+
// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
// if that parameter does not exist.
std::string fs_mgr_get_slot_suffix() {
@@ -31,36 +53,24 @@
}
// Updates |fstab| for slot_suffix. Returns true on success, false on error.
-bool fs_mgr_update_for_slotselect(struct fstab *fstab) {
- int n;
+bool fs_mgr_update_for_slotselect(Fstab* fstab) {
std::string ab_suffix;
- for (n = 0; n < fstab->num_entries; n++) {
- fstab_rec& record = fstab->recs[n];
- if (record.fs_mgr_flags & MF_SLOTSELECT) {
- if (ab_suffix.empty()) {
- ab_suffix = fs_mgr_get_slot_suffix();
- // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
- if (ab_suffix.empty()) return false;
- }
-
- char* new_blk_device;
- if (asprintf(&new_blk_device, "%s%s", record.blk_device, ab_suffix.c_str()) <= 0) {
- return false;
- }
- free(record.blk_device);
- record.blk_device = new_blk_device;
-
- char* new_partition_name;
- if (record.logical_partition_name) {
- if (asprintf(&new_partition_name, "%s%s", record.logical_partition_name,
- ab_suffix.c_str()) <= 0) {
- return false;
- }
- free(record.logical_partition_name);
- record.logical_partition_name = new_partition_name;
- }
+ for (auto& entry : *fstab) {
+ if (!entry.fs_mgr_flags.slot_select && !entry.fs_mgr_flags.slot_select_other) {
+ continue;
}
+
+ if (ab_suffix.empty()) {
+ ab_suffix = fs_mgr_get_slot_suffix();
+ // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
+ if (ab_suffix.empty()) return false;
+ }
+
+ const auto& update_suffix =
+ entry.fs_mgr_flags.slot_select ? ab_suffix : other_suffix(ab_suffix);
+ entry.blk_device = entry.blk_device + update_suffix;
+ entry.logical_partition_name = entry.logical_partition_name + update_suffix;
}
return true;
}
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
new file mode 100644
index 0000000..830f0dd
--- /dev/null
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <selinux/selinux.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fs_mgr_overlayfs.h>
+#include <fs_mgr_vendor_overlay.h>
+#include <fstab/fstab.h>
+
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+
+namespace {
+
+// The order of the list means the priority to show the files in the directory.
+// The last one has the highest priority.
+const std::vector<const std::string> kVendorOverlaySourceDirs = {
+ "/system/vendor_overlay/",
+ "/product/vendor_overlay/",
+};
+const auto kVndkVersionPropertyName = "ro.vndk.version"s;
+const auto kVendorTopDir = "/vendor/"s;
+const auto kLowerdirOption = "lowerdir="s;
+
+std::vector<std::pair<std::string, std::string>> fs_mgr_get_vendor_overlay_dirs(
+ const std::string& vndk_version) {
+ std::vector<std::pair<std::string, std::string>> vendor_overlay_dirs;
+ for (const auto& vendor_overlay_source : kVendorOverlaySourceDirs) {
+ const auto overlay_top = vendor_overlay_source + vndk_version;
+ std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),
+ closedir);
+ if (!vendor_overlay_top) continue;
+
+ // Vendor overlay root for current vendor version found!
+ LINFO << "vendor overlay root: " << overlay_top;
+
+ struct dirent* dp;
+ while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {
+ if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {
+ continue;
+ }
+ vendor_overlay_dirs.emplace_back(overlay_top, dp->d_name);
+ }
+ }
+ return vendor_overlay_dirs;
+}
+
+bool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& mount_point) {
+ const auto [overlay_top, mount_dir] = mount_point;
+ const auto vendor_mount_point = kVendorTopDir + mount_dir;
+ LINFO << "vendor overlay mount on " << vendor_mount_point;
+
+ const auto target_context = fs_mgr_get_context(vendor_mount_point);
+ if (target_context.empty()) {
+ PERROR << " failed: cannot find the target vendor mount point";
+ return false;
+ }
+ const auto source_directory = overlay_top + "/" + mount_dir;
+ const auto source_context = fs_mgr_get_context(source_directory);
+ if (target_context != source_context) {
+ LERROR << " failed: source and target contexts do not match (source:" << source_context
+ << ", target:" << target_context << ")";
+ return false;
+ }
+
+ auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
+ options += ",override_creds=off";
+ }
+ auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
+ options + ")=";
+ auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+ options.c_str());
+ if (ret) {
+ PERROR << report << ret;
+ return false;
+ } else {
+ LINFO << report << ret;
+ return true;
+ }
+}
+
+} // namespace
+
+// Since the vendor overlay requires to know the version of the vendor partition,
+// it is not possible to mount vendor overlay at the first stage that cannot
+// initialize properties.
+// To read the properties, vendor overlay must be mounted at the second stage, right
+// after "property_load_boot_defaults()" is called.
+bool fs_mgr_vendor_overlay_mount_all() {
+ // To read the property, it must be called at the second init stage after the default
+ // properties are loaded.
+ static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
+ if (vndk_version.empty()) {
+ LINFO << "vendor overlay: vndk version not defined";
+ return false;
+ }
+
+ const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
+ if (vendor_overlay_dirs.empty()) return true;
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+ LINFO << "vendor overlay: kernel does not support overlayfs";
+ return false;
+ }
+
+ // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor
+ auto ret = true;
+ for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {
+ if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {
+ ret = false;
+ }
+ }
+ return ret;
+}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 2727a6d..efa2180 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,6 +35,7 @@
#include <android-base/unique_fd.h>
#include <crypto_utils/android_pubkey.h>
#include <cutils/properties.h>
+#include <fs_mgr/file_wait.h>
#include <libdm/dm.h>
#include <logwrap/logwrap.h>
#include <openssl/obj_mac.h>
@@ -44,8 +45,12 @@
#include "fec/io.h"
#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
#include "fs_mgr_priv.h"
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
#define VERITY_TABLE_RSA_KEY "/verity_key"
#define VERITY_TABLE_HASH_IDX 8
#define VERITY_TABLE_SALT_IDX 9
@@ -271,248 +276,6 @@
return 0;
}
-static int check_verity_restart(const char *fname)
-{
- char buffer[VERITY_KMSG_BUFSIZE + 1];
- int fd;
- int rc = 0;
- ssize_t size;
- struct stat s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- if (errno != ENOENT) {
- PERROR << "Failed to open " << fname;
- }
- goto out;
- }
-
- if (fstat(fd, &s) == -1) {
- PERROR << "Failed to fstat " << fname;
- goto out;
- }
-
- size = VERITY_KMSG_BUFSIZE;
-
- if (size > s.st_size) {
- size = s.st_size;
- }
-
- if (lseek(fd, s.st_size - size, SEEK_SET) == -1) {
- PERROR << "Failed to lseek " << (intmax_t)(s.st_size - size) << " " << fname;
- goto out;
- }
-
- if (!android::base::ReadFully(fd, buffer, size)) {
- PERROR << "Failed to read " << size << " bytes from " << fname;
- goto out;
- }
-
- buffer[size] = '\0';
-
- if (strstr(buffer, VERITY_KMSG_RESTART) != NULL) {
- rc = 1;
- }
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int was_verity_restart()
-{
- static const char* files[] = {
- // clang-format off
- "/sys/fs/pstore/console-ramoops-0",
- "/sys/fs/pstore/console-ramoops",
- "/proc/last_kmsg",
- NULL
- // clang-format on
- };
- int i;
-
- for (i = 0; files[i]; ++i) {
- if (check_verity_restart(files[i])) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static int metadata_add(FILE *fp, long start, const char *tag,
- unsigned int length, off64_t *offset)
-{
- if (fseek(fp, start, SEEK_SET) < 0 ||
- fprintf(fp, "%s %u\n", tag, length) < 0) {
- return -1;
- }
-
- *offset = ftell(fp);
-
- if (fseek(fp, length, SEEK_CUR) < 0 ||
- fprintf(fp, METADATA_EOD " 0\n") < 0) {
- return -1;
- }
-
- return 0;
-}
-
-static int metadata_find(const char *fname, const char *stag,
- unsigned int slength, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int rc = -1;
- int n;
- long start = 0x4000; /* skip cryptfs metadata area */
- uint32_t magic;
- unsigned int length = 0;
-
- if (!fname) {
- return -1;
- }
-
- auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
-
- if (!fp) {
- PERROR << "Failed to open " << fname;
- return -1;
- }
-
- /* check magic */
- if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to read magic from " << fname;
- return -1;
- }
-
- if (magic != METADATA_MAGIC) {
- magic = METADATA_MAGIC;
-
- if (fseek(fp.get(), start, SEEK_SET) < 0 ||
- fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
- PERROR << "Failed to write magic to " << fname;
- return -1;
- }
-
- rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to add metadata to " << fname;
- }
-
- return rc;
- }
-
- start += sizeof(magic);
-
- while (1) {
- n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
-
- if (n == 2 && strcmp(tag, METADATA_EOD)) {
- /* found a tag */
- start = ftell(fp.get());
-
- if (!strcmp(tag, stag) && length == slength) {
- *offset = start;
- return 0;
- }
-
- start += length;
-
- if (fseek(fp.get(), length, SEEK_CUR) < 0) {
- PERROR << "Failed to seek " << fname;
- return -1;
- }
- } else {
- rc = metadata_add(fp.get(), start, stag, slength, offset);
- if (rc < 0) {
- PERROR << "Failed to write metadata to " << fname;
- }
- return rc;
- }
- }
-}
-
-static int write_verity_state(const char *fname, off64_t offset, int32_t mode)
-{
- int fd;
- int rc = -1;
- struct verity_state s = { VERITY_STATE_HEADER, VERITY_STATE_VERSION, mode };
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_WRONLY | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pwrite64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to write " << sizeof(s) << " bytes to " << fname
- << " to offset " << offset;
- goto out;
- }
-
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
-static int read_verity_state(const char *fname, off64_t offset, int *mode)
-{
- int fd = -1;
- int rc = -1;
- struct verity_state s;
-
- fd = TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fname;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, &s, sizeof(s), offset)) != sizeof(s)) {
- PERROR << "Failed to read " << sizeof(s) << " bytes from " << fname
- << " offset " << offset;
- goto out;
- }
-
- if (s.header != VERITY_STATE_HEADER) {
- /* space allocated, but no state written. write default state */
- *mode = VERITY_MODE_DEFAULT;
- rc = write_verity_state(fname, offset, *mode);
- goto out;
- }
-
- if (s.version != VERITY_STATE_VERSION) {
- LERROR << "Unsupported verity state version (" << s.version << ")";
- goto out;
- }
-
- if (s.mode < VERITY_MODE_EIO ||
- s.mode > VERITY_MODE_LAST) {
- LERROR << "Unsupported verity mode (" << s.mode << ")";
- goto out;
- }
-
- *mode = s.mode;
- rc = 0;
-
-out:
- if (fd != -1) {
- close(fd);
- }
-
- return rc;
-}
-
static int read_partition(const char *path, uint64_t size)
{
char buf[READ_BUF_SIZE];
@@ -536,125 +299,23 @@
return 0;
}
-static int compare_last_signature(struct fstab_rec *fstab, int *match)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
- int fd = -1;
- int rc = -1;
- off64_t offset = 0;
- struct fec_handle *f = NULL;
- struct fec_verity_metadata verity;
- uint8_t curr[SHA256_DIGEST_LENGTH];
- uint8_t prev[SHA256_DIGEST_LENGTH];
-
- *match = 1;
-
- if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
- FEC_DEFAULT_ROOTS) == -1) {
- PERROR << "Failed to open '" << fstab->blk_device << "'";
- return rc;
- }
-
- // read verity metadata
- if (fec_verity_get_metadata(f, &verity) == -1) {
- PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
- goto out;
- }
-
- SHA256(verity.signature, sizeof(verity.signature), curr);
-
- if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
- basename(fstab->mount_point)) >= (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << fstab->mount_point;
- goto out;
- }
-
- if (metadata_find(fstab->verity_loc, tag, SHA256_DIGEST_LENGTH,
- &offset) < 0) {
- goto out;
- }
-
- fd = TEMP_FAILURE_RETRY(open(fstab->verity_loc, O_RDWR | O_SYNC | O_CLOEXEC));
-
- if (fd == -1) {
- PERROR << "Failed to open " << fstab->verity_loc;
- goto out;
- }
-
- if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev),
- offset)) != sizeof(prev)) {
- PERROR << "Failed to read " << sizeof(prev) << " bytes from "
- << fstab->verity_loc << " offset " << offset;
- goto out;
- }
-
- *match = !memcmp(curr, prev, SHA256_DIGEST_LENGTH);
-
- if (!*match) {
- /* update current signature hash */
- if (TEMP_FAILURE_RETRY(pwrite64(fd, curr, sizeof(curr),
- offset)) != sizeof(curr)) {
- PERROR << "Failed to write " << sizeof(curr) << " bytes to "
- << fstab->verity_loc << " offset " << offset;
- goto out;
- }
- }
-
- rc = 0;
-
-out:
- fec_close(f);
- return rc;
-}
-
-static int get_verity_state_offset(struct fstab_rec *fstab, off64_t *offset)
-{
- char tag[METADATA_TAG_MAX_LENGTH + 1];
-
- if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s",
- basename(fstab->mount_point)) >= (int)sizeof(tag)) {
- LERROR << "Metadata tag name too long for " << fstab->mount_point;
- return -1;
- }
-
- return metadata_find(fstab->verity_loc, tag, sizeof(struct verity_state),
- offset);
-}
-
-int load_verity_state(struct fstab_rec* fstab, int* mode) {
- int match = 0;
- off64_t offset = 0;
-
- /* unless otherwise specified, use EIO mode */
+bool fs_mgr_load_verity_state(int* mode) {
+ // unless otherwise specified, use EIO mode.
*mode = VERITY_MODE_EIO;
- /* use the kernel parameter if set */
- std::string veritymode;
- if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
- if (veritymode == "enforcing") {
- *mode = VERITY_MODE_DEFAULT;
- }
- return 0;
+ // The bootloader communicates verity mode via the kernel commandline
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ return false;
}
- if (get_verity_state_offset(fstab, &offset) < 0) {
- /* fall back to stateless behavior */
- return 0;
- }
-
- if (was_verity_restart()) {
- /* device was restarted after dm-verity detected a corrupted
- * block, so use EIO mode */
- return write_verity_state(fstab->verity_loc, offset, *mode);
- }
-
- if (!compare_last_signature(fstab, &match) && !match) {
- /* partition has been reflashed, reset dm-verity state */
+ if (verity_mode == "enforcing") {
*mode = VERITY_MODE_DEFAULT;
- return write_verity_state(fstab->verity_loc, offset, *mode);
+ } else if (verity_mode == "logging") {
+ *mode = VERITY_MODE_LOGGING;
}
- return read_verity_state(fstab->verity_loc, offset, mode);
+ return true;
}
// Update the verity table using the actual block device path.
@@ -716,8 +377,7 @@
// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
// verity device to get created before return
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
-{
+int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
int retval = FS_MGR_SETUP_VERITY_FAIL;
int fd = -1;
std::string verity_blk_name;
@@ -725,20 +385,20 @@
struct fec_verity_metadata verity;
struct verity_table_params params = { .table = NULL };
- const std::string mount_point(basename(fstab->mount_point));
+ const std::string mount_point(basename(entry->mount_point.c_str()));
bool verified_at_boot = false;
android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
- FEC_DEFAULT_ROOTS) < 0) {
- PERROR << "Failed to open '" << fstab->blk_device << "'";
+ if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
+ 0) {
+ PERROR << "Failed to open '" << entry->blk_device << "'";
return retval;
}
// read verity metadata
if (fec_verity_get_metadata(f, &verity) < 0) {
- PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+ PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
// Allow verity disabled when the device is unlocked without metadata
if (fs_mgr_is_device_unlocked()) {
retval = FS_MGR_SETUP_VERITY_SKIPPED;
@@ -760,9 +420,9 @@
params.ecc.valid = false;
}
- params.ecc_dev = fstab->blk_device;
+ params.ecc_dev = entry->blk_device.c_str();
- if (load_verity_state(fstab, ¶ms.mode) < 0) {
+ if (!fs_mgr_load_verity_state(¶ms.mode)) {
/* if accessing or updating the state failed, switch to the default
* safe mode. This makes sure the device won't end up in an endless
* restart loop, and no corrupted data will be exposed to userspace
@@ -803,8 +463,8 @@
<< " (mode " << params.mode << ")";
// Update the verity params using the actual block device path
- update_verity_table_blk_device(fstab->blk_device, ¶ms.table,
- fstab->fs_mgr_flags & MF_SLOTSELECT);
+ update_verity_table_blk_device(entry->blk_device, ¶ms.table,
+ entry->fs_mgr_flags.slot_select);
// load the verity mapping table
if (load_verity_table(dm, mount_point, verity.data_size, ¶ms, format_verity_table) == 0) {
@@ -848,31 +508,29 @@
}
// mark the underlying block device as read-only
- fs_mgr_set_blk_ro(fstab->blk_device);
+ fs_mgr_set_blk_ro(entry->blk_device);
// Verify the entire partition in one go
// If there is an error, allow it to mount as a normal verity partition.
- if (fstab->fs_mgr_flags & MF_VERIFYATBOOT) {
- LINFO << "Verifying partition " << fstab->blk_device << " at boot";
+ if (entry->fs_mgr_flags.verify_at_boot) {
+ LINFO << "Verifying partition " << entry->blk_device << " at boot";
int err = read_partition(verity_blk_name.c_str(), verity.data_size);
if (!err) {
- LINFO << "Verified verity partition "
- << fstab->blk_device << " at boot";
+ LINFO << "Verified verity partition " << entry->blk_device << " at boot";
verified_at_boot = true;
}
}
// assign the new verity block device as the block device
if (!verified_at_boot) {
- free(fstab->blk_device);
- fstab->blk_device = strdup(verity_blk_name.c_str());
+ entry->blk_device = verity_blk_name;
} else if (!dm.DeleteDevice(mount_point)) {
LERROR << "Failed to remove verity device " << mount_point.c_str();
goto out;
}
// make sure we've set everything up properly
- if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
+ if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
goto out;
}
@@ -888,3 +546,12 @@
return retval;
}
+
+bool fs_mgr_teardown_verity(FstabEntry* entry) {
+ const std::string mount_point(basename(entry->mount_point.c_str()));
+ if (!android::fs_mgr::UnmapDevice(mount_point)) {
+ return false;
+ }
+ LINFO << "Unmapped verity device " << mount_point;
+ return true;
+}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 5dabe76..bdec7be 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CORE_FS_MGR_H
-#define __CORE_FS_MGR_H
+#pragma once
#include <stdio.h>
#include <stdint.h>
@@ -50,10 +49,6 @@
MOUNT_MODE_LATE = 2
};
-// Callback function for verity status
-typedef void fs_mgr_verity_state_callback(fstab_rec* fstab, const char* mount_point, int mode,
- int status);
-
#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7
#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
@@ -63,35 +58,53 @@
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1)
-int fs_mgr_mount_all(fstab* fstab, int mount_mode);
+// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
+int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
#define FS_MGR_DOMNT_SUCCESS 0
-
-int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point);
-int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
- bool need_cp);
-int fs_mgr_do_mount_one(fstab_rec* rec);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
+ char* tmp_mount_point);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
+ char* tmp_mount_point, bool need_cp);
+int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
+ const std::string& mount_point = "");
int fs_mgr_do_tmpfs_mount(const char *n_name);
-fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab);
-void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size);
bool fs_mgr_load_verity_state(int* mode);
-bool fs_mgr_update_verity_state(std::function<fs_mgr_verity_state_callback> callback);
-int fs_mgr_swapon_all(struct fstab *fstab);
-bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
+// Returns true if verity is enabled on this particular FstabEntry.
+bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
+bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
+bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
-int fs_mgr_do_format(fstab_rec* fstab, bool reserve_footer);
+// Returns true if the given fstab entry has verity enabled, *and* the verity
+// device is in "check_at_most_once" mode.
+bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);
+
+int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry, bool reserve_footer);
#define FS_MGR_SETUP_VERITY_SKIPPED (-3)
#define FS_MGR_SETUP_VERITY_DISABLED (-2)
#define FS_MGR_SETUP_VERITY_FAIL (-1)
#define FS_MGR_SETUP_VERITY_SUCCESS 0
-int fs_mgr_setup_verity(fstab_rec* fstab, bool wait_for_verity_dev);
+int fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity_dev);
// Return the name of the super partition if it exists. If a slot number is
// specified, the super partition for the corresponding metadata slot will be
// returned. Otherwise, it will use the current slot.
std::string fs_mgr_get_super_partition_name(int slot = -1);
-#endif /* __CORE_FS_MGR_H */
+enum FsMgrUmountStatus : int {
+ SUCCESS = 0,
+ ERROR_UNKNOWN = 1 << 0,
+ ERROR_UMOUNT = 1 << 1,
+ ERROR_VERITY = 1 << 2,
+ ERROR_DEVICE_MAPPER = 1 << 3,
+};
+// fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular,
+// it destroys verity devices from device mapper after the device is unmounted.
+int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
+
+// Finds the dm_bow device on which this block device is stacked, or returns
+// empty string
+std::string fs_mgr_find_bow_device(const std::string& block_device);
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
new file mode 100644
index 0000000..74d160e
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <string>
+
+namespace android {
+namespace fs_mgr {
+
+// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
+// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
+// be a valid directory.
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+// Wait at most |relative_timeout| milliseconds for |path| to stop existing.
+// Note that this only returns true if the inode itself no longer exists, i.e.,
+// all outstanding file descriptors have been closed.
+bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
new file mode 100644
index 0000000..65c59cf
--- /dev/null
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_mgr.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match
+// only, so it attempts the prefixes recursively (e.g. "/cache/recovery/last_log",
+// "/cache/recovery", "/cache", "/" for a given path of "/cache/recovery/last_log") and returns the
+// first match or nullptr.
+FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
+
+// Make sure that the volume 'path' is on is mounted.
+// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
+// fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
+// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can
+// call umount(mount_point) to unmount.
+// Returns true on success (volume is mounted).
+bool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = "");
+
+// Make sure that the volume 'path' is on is unmounted. Returns true on
+// success (volume is unmounted).
+bool EnsurePathUnmounted(Fstab* fstab, const std::string& path);
+
+// Return "/system" if it is in default fstab, otherwise "/".
+std::string GetSystemRoot();
+
+// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted
+// functions.
+bool LogicalPartitionsMapped();
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/include/fs_mgr_avb.h b/fs_mgr/include/fs_mgr_avb.h
deleted file mode 100644
index 73a22c8..0000000
--- a/fs_mgr/include/fs_mgr_avb.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CORE_FS_MGR_AVB_H
-#define __CORE_FS_MGR_AVB_H
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <libavb/libavb.h>
-
-#include "fs_mgr.h"
-
-enum class SetUpAvbHashtreeResult {
- kSuccess = 0,
- kFail,
- kDisabled,
-};
-
-class FsManagerAvbOps;
-
-class FsManagerAvbHandle;
-using FsManagerAvbUniquePtr = std::unique_ptr<FsManagerAvbHandle>;
-
-using ByNameSymlinkMap = std::map<std::string, std::string>;
-
-// Provides a factory method to return a unique_ptr pointing to itself and the
-// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
-// descriptors to load verity table into kernel through ioctl.
-class FsManagerAvbHandle {
- public:
- // The factory method to return a FsManagerAvbUniquePtr that holds
- // the verified AVB (external/avb) metadata of all verified partitions
- // in avb_slot_data_.vbmeta_images[].
- //
- // The metadata is checked against the following values from /proc/cmdline.
- // - androidboot.vbmeta.{hash_alg, size, digest}.
- //
- // A typical usage will be:
- // - FsManagerAvbUniquePtr handle = FsManagerAvbHandle::Open();
- //
- // There are two overloaded Open() functions with a single parameter.
- // The argument can be a ByNameSymlinkMap describing the mapping from partition
- // name to by-name symlink, or a fstab file to which the ByNameSymlinkMap is
- // constructed from. e.g.,
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a ->
- // - ByNameSymlinkMap["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
- //
- // Possible return values:
- // - nullptr: any error when reading and verifying the metadata,
- // e.g., I/O error, digest value mismatch, size mismatch, etc.
- //
- // - a valid unique_ptr with status kAvbHandleHashtreeDisabled:
- // to support the existing 'adb disable-verity' feature in Android.
- // It's very helpful for developers to make the filesystem writable to
- // allow replacing binaries on the device.
- //
- // - a valid unique_ptr with status kAvbHandleVerificationDisabled:
- // to support 'avbctl disable-verification': only the top-level
- // vbmeta is read, vbmeta structs in other partitions are not processed.
- // It's needed to bypass AVB when using the generic system.img to run
- // VTS for project Treble.
- //
- // - a valid unique_ptr with status kAvbHandleVerificationError:
- // there is verification error when libavb loads vbmeta from each
- // partition. This is only allowed when the device is unlocked.
- //
- // - a valid unique_ptr with status kAvbHandleSuccess: the metadata
- // is verified and can be trusted.
- //
- static FsManagerAvbUniquePtr Open(const fstab& fstab);
- static FsManagerAvbUniquePtr Open(ByNameSymlinkMap&& by_name_symlink_map);
-
- // Sets up dm-verity on the given fstab entry.
- // The 'wait_for_verity_dev' parameter makes this function wait for the
- // verity device to get created before return.
- //
- // Return value:
- // - kSuccess: successfully loads dm-verity table into kernel.
- // - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
- // failed to get the HASHTREE descriptor, runtime error when set up
- // device-mapper, etc.
- // - kDisabled: hashtree is disabled.
- SetUpAvbHashtreeResult SetUpAvbHashtree(fstab_rec* fstab_entry, bool wait_for_verity_dev);
-
- const std::string& avb_version() const { return avb_version_; }
-
- FsManagerAvbHandle(const FsManagerAvbHandle&) = delete; // no copy
- FsManagerAvbHandle& operator=(const FsManagerAvbHandle&) = delete; // no assignment
-
- FsManagerAvbHandle(FsManagerAvbHandle&&) noexcept = delete; // no move
- FsManagerAvbHandle& operator=(FsManagerAvbHandle&&) noexcept = delete; // no move assignment
-
- ~FsManagerAvbHandle() {
- if (avb_slot_data_) {
- avb_slot_verify_data_free(avb_slot_data_);
- }
- };
-
- private:
- enum AvbHandleStatus {
- kAvbHandleSuccess = 0,
- kAvbHandleUninitialized,
- kAvbHandleHashtreeDisabled,
- kAvbHandleVerificationDisabled,
- kAvbHandleVerificationError,
- };
-
- FsManagerAvbHandle() : avb_slot_data_(nullptr), status_(kAvbHandleUninitialized) {}
- static FsManagerAvbUniquePtr DoOpen(FsManagerAvbOps* avb_ops);
-
- AvbSlotVerifyData* avb_slot_data_;
- AvbHandleStatus status_;
- std::string avb_version_;
-};
-
-#endif /* __CORE_FS_MGR_AVB_H */
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 08f4554..a1dc2dc 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -33,11 +33,20 @@
#include <vector>
#include <libdm/dm.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
namespace android {
namespace fs_mgr {
+// Read metadata from the current slot.
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);
+
+// Create block devices for all logical partitions in the given metadata. The
+// metadata must have been read from the current slot.
+bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& block_device);
+
+// Create block devices for all logical partitions. This is a convenience
+// method for ReadMetadata and CreateLogicalPartitions.
bool CreateLogicalPartitions(const std::string& block_device);
// Create a block device for a single logical partition, given metadata and
@@ -51,9 +60,15 @@
const std::string& partition_name, bool force_writable,
const std::chrono::milliseconds& timeout_ms, std::string* path);
+// Same as above, but with a given metadata object. Care should be taken that
+// the metadata represents a valid partition layout.
+bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
+ const std::string& partition_name, bool force_writable,
+ const std::chrono::milliseconds& timeout_ms, std::string* path);
+
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool DestroyLogicalPartition(const std::string& name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 550dd18..9a7381f 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,9 +21,20 @@
#include <string>
#include <vector>
-bool fs_mgr_overlayfs_mount_all(fstab* fstab);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
+bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
+std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
- bool* change = nullptr);
+ bool* change = nullptr, bool force = true);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
+bool fs_mgr_overlayfs_is_setup();
bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
+std::string fs_mgr_get_context(const std::string& mount_point);
+
+enum class OverlayfsValidResult {
+ kNotSupported = 0,
+ kOk,
+ kOverrideCredsRequired,
+};
+OverlayfsValidResult fs_mgr_overlayfs_valid();
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/fs_mgr/include/fs_mgr_vendor_overlay.h
similarity index 77%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to fs_mgr/include/fs_mgr_vendor_overlay.h
index 410d379..9771a0c 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/fs_mgr/include/fs_mgr_vendor_overlay.h
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include <fstab/fstab.h>
+
+bool fs_mgr_vendor_overlay_mount_all();
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index bb40511..c7193ab 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -14,84 +14,103 @@
* limitations under the License.
*/
-#ifndef __CORE_FS_TAB_H
-#define __CORE_FS_TAB_H
+#pragma once
-#include <linux/dm-ioctl.h>
-#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
+#include <sys/types.h>
#include <set>
#include <string>
-
-/*
- * The entries must be kept in the same order as they were seen in the fstab.
- * Unless explicitly requested, a lookup on mount point should always
- * return the 1st one.
- */
-struct fstab {
- int num_entries;
- struct fstab_rec* recs;
-};
-
-struct fstab_rec {
- char* blk_device;
- char* logical_partition_name;
- char* mount_point;
- char* fs_type;
- unsigned long flags;
- char* fs_options;
- int fs_mgr_flags;
- char* key_loc;
- char* key_dir;
- char* verity_loc;
- long long length;
- char* label;
- int partnum;
- int swap_prio;
- int max_comp_streams;
- unsigned int zram_size;
- uint64_t reserved_size;
- unsigned int file_contents_mode;
- unsigned int file_names_mode;
- unsigned int erase_blk_size;
- unsigned int logical_blk_size;
- char* sysfs_path;
-};
-
-struct fstab* fs_mgr_read_fstab_default();
-struct fstab* fs_mgr_read_fstab_dt();
-struct fstab* fs_mgr_read_fstab(const char* fstab_path);
-void fs_mgr_free_fstab(struct fstab* fstab);
-
-int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
- const char* blk_device);
-struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path);
-int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
-int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
-int fs_mgr_is_verified(const struct fstab_rec* fstab);
-int fs_mgr_is_verifyatboot(const struct fstab_rec* fstab);
-int fs_mgr_is_avb(const struct fstab_rec* fstab);
-int fs_mgr_is_encryptable(const struct fstab_rec* fstab);
-int fs_mgr_is_file_encrypted(const struct fstab_rec* fstab);
-void fs_mgr_get_file_encryption_modes(const struct fstab_rec* fstab, const char** contents_mode_ret,
- const char** filenames_mode_ret);
-int fs_mgr_is_convertible_to_fbe(const struct fstab_rec* fstab);
-int fs_mgr_is_noemulatedsd(const struct fstab_rec* fstab);
-int fs_mgr_is_notrim(const struct fstab_rec* fstab);
-int fs_mgr_is_formattable(const struct fstab_rec* fstab);
-int fs_mgr_is_slotselect(const struct fstab_rec* fstab);
-int fs_mgr_is_nofail(const struct fstab_rec* fstab);
-int fs_mgr_is_latemount(const struct fstab_rec* fstab);
-int fs_mgr_is_quota(const struct fstab_rec* fstab);
-int fs_mgr_is_logical(const struct fstab_rec* fstab);
-int fs_mgr_is_checkpoint(const struct fstab_rec* fstab);
-int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
-int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
-int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
+#include <vector>
std::string fs_mgr_get_slot_suffix();
-std::set<std::string> fs_mgr_get_boot_devices();
+std::string fs_mgr_get_other_slot_suffix();
-#endif /* __CORE_FS_TAB_H */
+namespace android {
+namespace fs_mgr {
+
+struct FstabEntry {
+ std::string blk_device;
+ std::string logical_partition_name;
+ std::string mount_point;
+ std::string fs_type;
+ unsigned long flags = 0;
+ std::string fs_options;
+ std::string key_loc;
+ std::string key_dir;
+ off64_t length = 0;
+ std::string label;
+ int partnum = -1;
+ int swap_prio = -1;
+ int max_comp_streams = 0;
+ off64_t zram_size = 0;
+ off64_t reserved_size = 0;
+ std::string file_contents_mode;
+ std::string file_names_mode;
+ off64_t erase_blk_size = 0;
+ off64_t logical_blk_size = 0;
+ std::string sysfs_path;
+ std::string vbmeta_partition;
+ std::string zram_loopback_path;
+ uint64_t zram_loopback_size = 512 * 1024 * 1024; // 512MB by default;
+ std::string zram_backing_dev_path;
+ std::string avb_keys;
+
+ struct FsMgrFlags {
+ bool wait : 1;
+ bool check : 1;
+ bool crypt : 1;
+ bool nonremovable : 1;
+ bool vold_managed : 1;
+ bool recovery_only : 1;
+ bool verify : 1;
+ bool force_crypt : 1;
+ bool no_emulated_sd : 1; // No emulated sdcard daemon; sd card is the only external
+ // storage.
+ bool no_trim : 1;
+ bool file_encryption : 1;
+ bool formattable : 1;
+ bool slot_select : 1;
+ bool force_fde_or_fbe : 1;
+ bool late_mount : 1;
+ bool no_fail : 1;
+ bool verify_at_boot : 1;
+ bool quota : 1;
+ bool avb : 1;
+ bool logical : 1;
+ bool checkpoint_blk : 1;
+ bool checkpoint_fs : 1;
+ bool first_stage_mount : 1;
+ bool slot_select_other : 1;
+ bool fs_verity : 1;
+ } fs_mgr_flags = {};
+
+ bool is_encryptable() const {
+ return fs_mgr_flags.crypt || fs_mgr_flags.force_crypt || fs_mgr_flags.force_fde_or_fbe;
+ }
+};
+
+// An Fstab is a collection of FstabEntry structs.
+// The entries must be kept in the same order as they were seen in the fstab.
+// Unless explicitly requested, a lookup on mount point should always return the 1st one.
+using Fstab = std::vector<FstabEntry>;
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
+bool ReadFstabFromDt(Fstab* fstab, bool log = true);
+bool ReadDefaultFstab(Fstab* fstab);
+bool SkipMountingPartitions(Fstab* fstab);
+
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
+
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
+
+std::set<std::string> GetBootDevices();
+
+// Return the name of the dm-verity device for the given fstab entry. This does
+// not check whether the device is valid or exists; it merely returns the
+// expected name.
+std::string GetVerityDeviceName(const FstabEntry& entry);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 22af123..4cdea71 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -18,24 +18,30 @@
name: "libdm",
defaults: ["fs_mgr_defaults"],
recovery_available: true,
+ host_supported: true,
export_include_dirs: ["include"],
- cflags: [
- // TODO(b/110035986): Allows us to create a skeleton of required classes
- "-Wno-unused-private-field",
- ],
srcs: [
"dm_table.cpp",
"dm_target.cpp",
"dm.cpp",
"loop_control.cpp",
+ "utility.cpp",
],
+ static_libs: [
+ "libext2_uuid",
+ ],
header_libs: [
"libbase_headers",
"liblog_headers",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_test {
@@ -44,6 +50,8 @@
static_libs: [
"libdm",
"libbase",
+ "libext2_uuid",
+ "libfs_mgr",
"liblog",
],
srcs: [
diff --git a/Android.mk b/fs_mgr/libdm/Android.mk
similarity index 73%
copy from Android.mk
copy to fs_mgr/libdm/Android.mk
index 7c57258..6aedc25 100644
--- a/Android.mk
+++ b/fs_mgr/libdm/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-LOCAL_PATH := $(my-dir)
-include $(call first-makefiles-under,$(LOCAL_PATH))
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLibdmTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libdm/AndroidTest.xml b/fs_mgr/libdm/AndroidTest.xml
new file mode 100644
index 0000000..b4e0c23
--- /dev/null
+++ b/fs_mgr/libdm/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLibdmTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsKernelLibdmTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/libdm_test/libdm_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/libdm_test/libdm_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4b57c7..a4e0d76 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,12 +20,22 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
namespace android {
namespace dm {
+using namespace std::literals;
+
DeviceMapper::DeviceMapper() : fd_(-1) {
fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd_ < 0) {
@@ -37,13 +47,13 @@
static DeviceMapper instance;
return instance;
}
+
// Creates a new device mapper device
-bool DeviceMapper::CreateDevice(const std::string& name) {
+bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
if (name.empty()) {
LOG(ERROR) << "Unnamed device mapper device creation is not supported";
return false;
}
-
if (name.size() >= DM_NAME_LEN) {
LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
return false;
@@ -51,6 +61,9 @@
struct dm_ioctl io;
InitIo(&io, name);
+ if (!uuid.empty()) {
+ snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
+ }
if (ioctl(fd_, DM_DEV_CREATE, &io)) {
PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
@@ -67,16 +80,6 @@
}
bool DeviceMapper::DeleteDevice(const std::string& name) {
- if (name.empty()) {
- LOG(ERROR) << "Unnamed device mapper device creation is not supported";
- return false;
- }
-
- if (name.size() >= DM_NAME_LEN) {
- LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
- return false;
- }
-
struct dm_ioctl io;
InitIo(&io, name);
@@ -93,9 +96,67 @@
return true;
}
-const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
- // TODO(b/110035986): Return the table, as read from the kernel instead
- return nullptr;
+static std::string GenerateUuid() {
+ uuid_t uuid_bytes;
+ uuid_generate(uuid_bytes);
+
+ char uuid_chars[37] = {};
+ uuid_unparse_lower(uuid_bytes, uuid_chars);
+
+ return std::string{uuid_chars};
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) {
+ std::string uuid = GenerateUuid();
+ if (!CreateDevice(name, uuid)) {
+ return false;
+ }
+
+ // We use the unique path for testing whether the device is ready. After
+ // that, it's safe to use the dm-N path which is compatible with callers
+ // that expect it to be formatted as such.
+ std::string unique_path;
+ if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
+ !GetDmDevicePathByName(name, path)) {
+ DeleteDevice(name);
+ return false;
+ }
+
+ if (timeout_ms <= std::chrono::milliseconds::zero()) {
+ return true;
+ }
+ if (!WaitForFile(unique_path, timeout_ms)) {
+ LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "Failed to get device path: " << name;
+ return false;
+ }
+
+ if (io.uuid[0] == '\0') {
+ LOG(ERROR) << "Device does not have a unique path: " << name;
+ return false;
+ }
+ *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
+ return true;
+}
+
+std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ return std::nullopt;
+ }
+ return Info(io.flags);
}
DmDeviceState DeviceMapper::GetState(const std::string& name) const {
@@ -110,12 +171,27 @@
return DmDeviceState::SUSPENDED;
}
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
- if (!CreateDevice(name)) {
+bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
+ if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
return false;
}
- if (!LoadTableAndActivate(name, table)) {
- DeleteDevice(name);
+
+ struct dm_ioctl io;
+ InitIo(&io, name);
+
+ if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
+
+ if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
+ PLOG(ERROR) << "DM_DEV_SUSPEND "
+ << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+ std::string ignore_path;
+ if (!CreateDevice(name, table, &ignore_path, 0ms)) {
return false;
}
return true;
@@ -203,12 +279,27 @@
}
next += vers->next;
data_size -= vers->next;
- vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
+ next);
}
return true;
}
+bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
+ std::vector<DmTargetTypeInfo> targets;
+ if (!GetAvailableTargets(&targets)) {
+ return false;
+ }
+ for (const auto& target : targets) {
+ if (target.name() == name) {
+ if (info) *info = target;
+ return true;
+ }
+ }
+ return false;
+}
+
bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
devices->clear();
@@ -287,24 +378,56 @@
return true;
}
-bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
- char buffer[4096];
- struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);
-
- InitIo(io, name);
- io->data_size = sizeof(buffer);
- io->data_start = sizeof(*io);
- if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
- PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
+bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
return false;
}
- if (io->flags & DM_BUFFER_FULL_FLAG) {
- PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
+ *dev = io.dev;
+ return true;
+}
+
+bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
+ dev_t num;
+ if (!GetDeviceNumber(name, &num)) {
return false;
}
+ *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
+ return true;
+}
+
+bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
+ return GetTable(name, 0, table);
+}
+
+bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
+ return GetTable(name, DM_STATUS_TABLE_FLAG, table);
+}
+
+// private methods of DeviceMapper
+bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
+ std::vector<TargetInfo>* table) {
+ std::vector<char> buffer;
+ struct dm_ioctl* io = nullptr;
+
+ for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
+ io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
+
+ InitIo(io, name);
+ io->data_size = buffer.size();
+ io->data_start = sizeof(*io);
+ io->flags = flags;
+ if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
+ PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
+ return false;
+ }
+ if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;
+ }
uint32_t cursor = io->data_start;
- uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
+ uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
for (uint32_t i = 0; i < io->target_count; i++) {
if (cursor + sizeof(struct dm_target_spec) > data_end) {
break;
@@ -312,14 +435,14 @@
// After each dm_target_spec is a status string. spec->next is an
// offset from |io->data_start|, and we clamp it to the size of our
// buffer.
- struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
+ struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);
uint32_t data_offset = cursor + sizeof(dm_target_spec);
uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
std::string data;
if (next_cursor > data_offset) {
// Note: we use c_str() to eliminate any extra trailing 0s.
- data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
+ data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
}
table->emplace_back(*spec, data);
cursor = next_cursor;
@@ -327,7 +450,6 @@
return true;
}
-// private methods of DeviceMapper
void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
memset(io, 0, sizeof(*io));
@@ -338,9 +460,17 @@
io->data_size = sizeof(*io);
io->data_start = 0;
if (!name.empty()) {
- strlcpy(io->name, name.c_str(), sizeof(io->name));
+ snprintf(io->name, sizeof(io->name), "%s", name.c_str());
}
}
+std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
+ if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
+ ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
+ return std::string{spec.target_type, static_cast<size_t>(length)};
+ }
+ return std::string{spec.target_type, sizeof(spec.target_type)};
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 7c18267..9152677 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -16,8 +16,13 @@
#include "libdm/dm_target.h"
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <libdm/dm.h>
@@ -42,7 +47,7 @@
struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
spec->sector_start = start();
spec->length = size();
- strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
+ snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
spec->next = (uint32_t)data.size();
return data;
}
@@ -115,5 +120,120 @@
return keyid_ + " " + block_device_;
}
+std::string DmTargetSnapshot::name() const {
+ if (mode_ == SnapshotStorageMode::Merge) {
+ return "snapshot-merge";
+ }
+ return "snapshot";
+}
+
+std::string DmTargetSnapshot::GetParameterString() const {
+ std::string mode;
+ switch (mode_) {
+ case SnapshotStorageMode::Persistent:
+ case SnapshotStorageMode::Merge:
+ // Note: "O" lets us query for overflow in the status message. This
+ // is only supported on kernels 4.4+. On earlier kernels, an overflow
+ // will be reported as "Invalid" in the status string.
+ mode = "P";
+ if (ReportsOverflow(name())) {
+ mode += "O";
+ }
+ break;
+ case SnapshotStorageMode::Transient:
+ mode = "N";
+ break;
+ default:
+ LOG(ERROR) << "DmTargetSnapshot unknown mode";
+ break;
+ }
+ return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
+}
+
+// Computes the percentage of complition for snapshot status.
+// @sectors_initial is the number of sectors_allocated stored right before
+// starting the merge.
+double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
+ uint64_t sectors_initial) {
+ uint64_t s = status.sectors_allocated;
+ uint64_t t = status.total_sectors;
+ uint64_t m = status.metadata_sectors;
+ uint64_t i = sectors_initial == 0 ? t : sectors_initial;
+
+ if (t <= s || i <= s) {
+ return 0.0;
+ }
+ if (s == 0 || t == 0 || s <= m) {
+ return 100.0;
+ }
+ return 100.0 / (i - m) * (i - s);
+}
+
+bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
+ DeviceMapper& dm = DeviceMapper::Instance();
+ DmTargetTypeInfo info;
+ if (!dm.GetTargetByName(target_type, &info)) {
+ return false;
+ }
+ if (target_type == "snapshot") {
+ return info.IsAtLeast(1, 15, 0);
+ }
+ if (target_type == "snapshot-merge") {
+ return info.IsAtLeast(1, 4, 0);
+ }
+ return false;
+}
+
+bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+ // Try to parse the line as it should be
+ int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
+ &status->total_sectors, &status->metadata_sectors);
+ if (args == 3) {
+ return true;
+ }
+ auto sections = android::base::Split(text, " ");
+ if (sections.size() == 0) {
+ LOG(ERROR) << "could not parse empty status";
+ return false;
+ }
+ // Error codes are: "Invalid", "Overflow" and "Merge failed"
+ if (sections.size() == 1) {
+ if (text == "Invalid" || text == "Overflow") {
+ status->error = text;
+ return true;
+ }
+ } else if (sections.size() == 2 && text == "Merge failed") {
+ status->error = text;
+ return true;
+ }
+ LOG(ERROR) << "could not parse snapshot status: wrong format";
+ return false;
+}
+
+std::string DmTargetCrypt::GetParameterString() const {
+ std::vector<std::string> argv = {
+ cipher_,
+ key_,
+ std::to_string(iv_sector_offset_),
+ device_,
+ std::to_string(device_sector_),
+ };
+
+ std::vector<std::string> extra_argv;
+ if (allow_discards_) extra_argv.emplace_back("allow_discards");
+ if (allow_encrypt_override_) extra_argv.emplace_back("allow_encrypt_override");
+ if (iv_large_sectors_) extra_argv.emplace_back("iv_large_sectors");
+ if (sector_size_) extra_argv.emplace_back("sector_size:" + std::to_string(sector_size_));
+
+ if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));
+
+ argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());
+ return android::base::Join(argv, " ");
+}
+
+std::string DmTargetDefaultKey::GetParameterString() const {
+ return cipher_ + " " + key_ + " " + blockdev_ + " " + std::to_string(start_sector_);
+}
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 70823c6..eed21dc 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <ctime>
+#include <iostream>
#include <map>
#include <thread>
@@ -35,21 +36,15 @@
#include "test_util.h"
using namespace std;
+using namespace std::chrono_literals;
using namespace android::dm;
using unique_fd = android::base::unique_fd;
TEST(libdm, HasMinimumTargets) {
+ DmTargetTypeInfo info;
+
DeviceMapper& dm = DeviceMapper::Instance();
- vector<DmTargetTypeInfo> targets;
- ASSERT_TRUE(dm.GetAvailableTargets(&targets));
-
- map<string, DmTargetTypeInfo> by_name;
- for (const auto& target : targets) {
- by_name[target.name()] = target;
- }
-
- auto iter = by_name.find("linear");
- EXPECT_NE(iter, by_name.end());
+ ASSERT_TRUE(dm.GetTargetByName("linear", &info));
}
// Helper to ensure that device mapper devices are released.
@@ -57,10 +52,10 @@
public:
TempDevice(const std::string& name, const DmTable& table)
: dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table);
+ valid_ = dm_.CreateDevice(name, table, &path_, 5s);
}
TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
other.valid_ = false;
}
~TempDevice() {
@@ -75,29 +70,7 @@
valid_ = false;
return dm_.DeleteDevice(name_);
}
- bool WaitForUdev() const {
- auto start_time = std::chrono::steady_clock::now();
- while (true) {
- if (!access(path().c_str(), F_OK)) {
- return true;
- }
- if (errno != ENOENT) {
- return false;
- }
- std::this_thread::sleep_for(50ms);
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
- if (elapsed >= 5s) {
- return false;
- }
- }
- }
- std::string path() const {
- std::string device_path;
- if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
- return "";
- }
- return device_path;
- }
+ std::string path() const { return path_; }
const std::string& name() const { return name_; }
bool valid() const { return valid_; }
@@ -114,6 +87,7 @@
private:
DeviceMapper& dm_;
std::string name_;
+ std::string path_;
bool valid_;
};
@@ -129,22 +103,31 @@
ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
- LoopDevice loop_a(tmp1);
+ LoopDevice loop_a(tmp1, 10s);
ASSERT_TRUE(loop_a.valid());
- LoopDevice loop_b(tmp2);
+ LoopDevice loop_b(tmp2, 10s);
ASSERT_TRUE(loop_b.valid());
// Define a 2-sector device, with each sector mapping to the first sector
// of one of our loop devices.
DmTable table;
- ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
- ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(1, 1, loop_b.device(), 0));
ASSERT_TRUE(table.valid());
TempDevice dev("libdm-test-dm-linear", table);
ASSERT_TRUE(dev.valid());
ASSERT_FALSE(dev.path().empty());
- ASSERT_TRUE(dev.WaitForUdev());
+
+ auto& dm = DeviceMapper::Instance();
+
+ dev_t dev_number;
+ ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
+ ASSERT_NE(dev_number, 0);
+
+ std::string dev_string;
+ ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
+ ASSERT_FALSE(dev_string.empty());
// Note: a scope is needed to ensure that there are no open descriptors
// when we go to close the device.
@@ -162,7 +145,6 @@
}
// Test GetTableStatus.
- DeviceMapper& dm = DeviceMapper::Instance();
vector<DeviceMapper::TargetInfo> targets;
ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
ASSERT_EQ(targets.size(), 2);
@@ -175,11 +157,43 @@
EXPECT_EQ(targets[1].spec.sector_start, 1);
EXPECT_EQ(targets[1].spec.length, 1);
+ // Test GetTargetType().
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
+ EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
+
// Normally the TestDevice destructor would delete this, but at least one
// test should ensure that device deletion works.
ASSERT_TRUE(dev.Destroy());
}
+TEST(libdm, DmSuspendResume) {
+ unique_fd tmp1(CreateTempFile("file_suspend_resume", 512));
+ ASSERT_GE(tmp1, 0);
+
+ LoopDevice loop_a(tmp1, 10s);
+ ASSERT_TRUE(loop_a.valid());
+
+ DmTable table;
+ ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+ ASSERT_TRUE(table.valid());
+
+ TempDevice dev("libdm-test-dm-suspend-resume", table);
+ ASSERT_TRUE(dev.valid());
+ ASSERT_FALSE(dev.path().empty());
+
+ auto& dm = DeviceMapper::Instance();
+
+ // Test Set and Get status of device.
+ vector<DeviceMapper::TargetInfo> targets;
+ ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+
+ ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::SUSPENDED));
+ ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::SUSPENDED);
+
+ ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::ACTIVE));
+ ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+}
+
TEST(libdm, DmVerityArgsAvb2) {
std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
std::string algorithm = "sha1";
@@ -201,3 +215,347 @@
"2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks";
EXPECT_EQ(target.GetParameterString(), expected);
}
+
+TEST(libdm, DmSnapshotArgs) {
+ DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target1.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target1.name(), "snapshot");
+
+ DmTargetSnapshot target2(0, 512, "base", "cow", SnapshotStorageMode::Transient, 8);
+ EXPECT_EQ(target2.GetParameterString(), "base cow N 8");
+ EXPECT_EQ(target2.name(), "snapshot");
+
+ DmTargetSnapshot target3(0, 512, "base", "cow", SnapshotStorageMode::Merge, 8);
+ if (DmTargetSnapshot::ReportsOverflow("snapshot-merge")) {
+ EXPECT_EQ(target3.GetParameterString(), "base cow PO 8");
+ } else {
+ EXPECT_EQ(target3.GetParameterString(), "base cow P 8");
+ }
+ EXPECT_EQ(target3.name(), "snapshot-merge");
+}
+
+TEST(libdm, DmSnapshotOriginArgs) {
+ DmTargetSnapshotOrigin target(0, 512, "base");
+ EXPECT_EQ(target.GetParameterString(), "base");
+ EXPECT_EQ(target.name(), "snapshot-origin");
+}
+
+class SnapshotTestHarness final {
+ public:
+ bool Setup();
+ bool Merge();
+
+ std::string origin_dev() const { return origin_dev_->path(); }
+ std::string snapshot_dev() const { return snapshot_dev_->path(); }
+
+ int base_fd() const { return base_fd_; }
+
+ static const uint64_t kBaseDeviceSize = 1024 * 1024;
+ static const uint64_t kCowDeviceSize = 1024 * 64;
+ static const uint64_t kSectorSize = 512;
+
+ private:
+ void SetupImpl();
+ void MergeImpl();
+
+ unique_fd base_fd_;
+ unique_fd cow_fd_;
+ unique_ptr<LoopDevice> base_loop_;
+ unique_ptr<LoopDevice> cow_loop_;
+ unique_ptr<TempDevice> origin_dev_;
+ unique_ptr<TempDevice> snapshot_dev_;
+ bool setup_ok_ = false;
+ bool merge_ok_ = false;
+};
+
+bool SnapshotTestHarness::Setup() {
+ SetupImpl();
+ return setup_ok_;
+}
+
+void SnapshotTestHarness::SetupImpl() {
+ base_fd_ = CreateTempFile("base_device", kBaseDeviceSize);
+ ASSERT_GE(base_fd_, 0);
+ cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
+ ASSERT_GE(cow_fd_, 0);
+
+ base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
+ ASSERT_TRUE(base_loop_->valid());
+ cow_loop_ = std::make_unique<LoopDevice>(cow_fd_, 10s);
+ ASSERT_TRUE(cow_loop_->valid());
+
+ DmTable origin_table;
+ ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device())));
+ ASSERT_TRUE(origin_table.valid());
+
+ origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
+ ASSERT_TRUE(origin_dev_->valid());
+ ASSERT_FALSE(origin_dev_->path().empty());
+
+ // chunk size = 4K blocks.
+ DmTable snap_table;
+ ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(
+ 0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),
+ SnapshotStorageMode::Persistent, 8)));
+ ASSERT_TRUE(snap_table.valid());
+
+ snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
+ ASSERT_TRUE(snapshot_dev_->valid());
+ ASSERT_FALSE(snapshot_dev_->path().empty());
+
+ setup_ok_ = true;
+}
+
+bool SnapshotTestHarness::Merge() {
+ MergeImpl();
+ return merge_ok_;
+}
+
+void SnapshotTestHarness::MergeImpl() {
+ DmTable merge_table;
+ ASSERT_TRUE(merge_table.AddTarget(
+ make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),
+ cow_loop_->device(), SnapshotStorageMode::Merge, 8)));
+ ASSERT_TRUE(merge_table.valid());
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ ASSERT_TRUE(dm.LoadTableAndActivate("libdm-test-dm-snapshot", merge_table));
+
+ while (true) {
+ vector<DeviceMapper::TargetInfo> status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &status));
+ ASSERT_EQ(status.size(), 1);
+ ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")),
+ 0);
+
+ DmTargetSnapshot::Status merge_status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));
+ ASSERT_TRUE(merge_status.error.empty());
+ if (merge_status.sectors_allocated == merge_status.metadata_sectors) {
+ break;
+ }
+
+ std::this_thread::sleep_for(250ms);
+ }
+
+ merge_ok_ = true;
+}
+
+bool CheckSnapshotAvailability() {
+ DmTargetTypeInfo info;
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.GetTargetByName("snapshot", &info)) {
+ cout << "snapshot module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-merge", &info)) {
+ cout << "snapshot-merge module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ if (!dm.GetTargetByName("snapshot-origin", &info)) {
+ cout << "snapshot-origin module not enabled; skipping test" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+TEST(libdm, DmSnapshot) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_GE(origin_fd, 0);
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Write to the first block of the snapshot device.
+ std::string data("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
+ ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+
+ // We should get the same data back from the snapshot device.
+ std::string read(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));
+ ASSERT_EQ(read, data);
+
+ // We should see the original data from the origin device.
+ std::string zeroes(data.size(), '\0');
+ ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));
+ ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // We should also see the original data from the base device.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, zeroes);
+
+ // Now, perform the merge and wait.
+ ASSERT_TRUE(harness.Merge());
+
+ // Reading from the base device should give us the modified data.
+ ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));
+ ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);
+ ASSERT_EQ(read, data);
+}
+
+TEST(libdm, DmSnapshotOverflow) {
+ if (!CheckSnapshotAvailability()) {
+ return;
+ }
+
+ SnapshotTestHarness harness;
+ ASSERT_TRUE(harness.Setup());
+
+ // Open the dm devices.
+ unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));
+ ASSERT_GE(snapshot_fd, 0);
+
+ // Fill the copy-on-write device until it overflows.
+ uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;
+ uint8_t byte = 1;
+ while (bytes_remaining) {
+ std::string data(4096, char(byte));
+ if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {
+ ASSERT_EQ(errno, EIO);
+ break;
+ }
+ bytes_remaining -= data.size();
+ }
+
+ // If writes succeed (because they are buffered), then we should expect an
+ // fsync to fail with EIO.
+ if (!bytes_remaining) {
+ ASSERT_EQ(fsync(snapshot_fd), -1);
+ ASSERT_EQ(errno, EIO);
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+
+ vector<DeviceMapper::TargetInfo> target_status;
+ ASSERT_TRUE(dm.GetTableStatus("libdm-test-dm-snapshot", &target_status));
+ ASSERT_EQ(target_status.size(), 1);
+ ASSERT_EQ(strncmp(target_status[0].spec.target_type, "snapshot", strlen("snapshot")), 0);
+
+ DmTargetSnapshot::Status status;
+ ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));
+ if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
+ ASSERT_EQ(status.error, "Overflow");
+ } else {
+ ASSERT_EQ(status.error, "Invalid");
+ }
+}
+
+TEST(libdm, ParseStatusText) {
+ DmTargetSnapshot::Status status;
+
+ // Bad inputs
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("X", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456 789", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456/789", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456/789", &status));
+ EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 / 456 789", &status));
+
+ // Good input
+ EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("123/456 789", &status));
+ EXPECT_EQ(status.sectors_allocated, 123);
+ EXPECT_EQ(status.total_sectors, 456);
+ EXPECT_EQ(status.metadata_sectors, 789);
+
+ // Known error codes
+ EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Invalid", &status));
+ EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Merge failed", &status));
+ EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Overflow", &status));
+}
+
+TEST(libdm, DmSnapshotMergePercent) {
+ DmTargetSnapshot::Status status;
+
+ // Correct input
+ status.sectors_allocated = 1000;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 0;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status), 1.0);
+
+ status.sectors_allocated = 500;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 0;
+ EXPECT_GE(DmTargetSnapshot::MergePercent(status), 49.0);
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status), 51.0);
+
+ status.sectors_allocated = 0;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 0;
+ EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+ status.sectors_allocated = 500;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 500;
+ EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+ status.sectors_allocated = 500;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 0;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status, 500), 1.0);
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status, 1000), 51.0);
+ EXPECT_GE(DmTargetSnapshot::MergePercent(status, 1000), 49.0);
+
+ // Robustness
+ status.sectors_allocated = 2000;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 0;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+ status.sectors_allocated = 2000;
+ status.total_sectors = 1000;
+ status.metadata_sectors = 2000;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+ status.sectors_allocated = 2000;
+ status.total_sectors = 0;
+ status.metadata_sectors = 2000;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+ status.sectors_allocated = 1000;
+ status.total_sectors = 0;
+ status.metadata_sectors = 1000;
+ EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);
+}
+
+TEST(libdm, CryptArgs) {
+ DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+ ASSERT_EQ(target1.name(), "crypt");
+ ASSERT_TRUE(target1.Valid());
+ ASSERT_EQ(target1.GetParameterString(), "sha1 abcdefgh 50 /dev/loop0 100");
+
+ DmTargetCrypt target2(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
+ target2.SetSectorSize(64);
+ target2.AllowDiscards();
+ target2.SetIvLargeSectors();
+ target2.AllowEncryptOverride();
+ ASSERT_EQ(target2.GetParameterString(),
+ "sha1 abcdefgh 50 /dev/loop0 100 4 allow_discards allow_encrypt_override "
+ "iv_large_sectors sector_size:64");
+}
+
+TEST(libdm, DefaultKeyArgs) {
+ DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
+ ASSERT_EQ(target.name(), "default-key");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 91f8bb4..c6b37cf 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -20,11 +20,14 @@
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
+#include <linux/types.h>
#include <stdint.h>
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -37,7 +40,7 @@
#define DM_VERSION2 (0)
#define DM_ALIGN_MASK (7)
-#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+#define DM_ALIGN(x) (((x) + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
namespace android {
namespace dm {
@@ -68,21 +71,66 @@
uint64_t dev_;
};
+ class Info {
+ uint32_t flags_;
+
+ public:
+ explicit Info(uint32_t flags) : flags_(flags) {}
+
+ bool IsActiveTablePresent() const { return flags_ & DM_ACTIVE_PRESENT_FLAG; }
+ bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }
+ bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }
+ bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }
+ bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; }
+ };
+
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
- // Reads the device mapper table from the device with given anme and
- // returns it in a DmTable object.
- const std::unique_ptr<DmTable> table(const std::string& name) const;
+ // Fetches and returns the complete state of the underlying device mapper
+ // device with given name.
+ std::optional<Info> GetDetailedInfo(const std::string& name) const;
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
DmDeviceState GetState(const std::string& name) const;
+ // Puts the given device to the specified status, which must be either:
+ // - SUSPENDED: suspend the device, or
+ // - ACTIVE: resumes the device.
+ bool ChangeState(const std::string& name, DmDeviceState state);
+
// Creates a device, loads the given table, and activates it. If the device
// is not able to be activated, it is destroyed, and false is returned.
+ // After creation, |path| contains the result of calling
+ // GetDmDevicePathByName, and the path is guaranteed to exist. If after
+ // |timeout_ms| the path is not available, the device will be deleted and
+ // this function will return false.
+ //
+ // This variant must be used when depending on the device path. The
+ // following manual sequence should not be used:
+ //
+ // 1. CreateDevice(name, table)
+ // 2. GetDmDevicePathByName(name, &path)
+ // 3. fs_mgr::WaitForFile(path, <timeout>)
+ //
+ // This sequence has a race condition where, if another process deletes a
+ // device, CreateDevice may acquire the same path. When this happens, the
+ // WaitForFile() may early-return since ueventd has not yet processed all
+ // of the outstanding udev events. The caller may unexpectedly get an
+ // ENOENT on a system call using the affected path.
+ //
+ // If |timeout_ms| is 0ms, then this function will return true whether or
+ // not |path| is available. It is the caller's responsibility to ensure
+ // there are no races.
+ bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms);
+
+ // Create a device and activate the given table, without waiting to acquire
+ // a valid path. If the caller will use GetDmDevicePathByName(), it should
+ // use the timeout variant above.
bool CreateDevice(const std::string& name, const DmTable& table);
// Loads the device mapper table from parameter into the underlying device
@@ -96,6 +144,10 @@
// successfully read and stored in 'targets'. Returns 'false' otherwise.
bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
+ // Finds a target by name and returns its information if found. |info| may
+ // be null to check for the existence of a target.
+ bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);
+
// Return 'true' if it can successfully read the list of device mapper block devices
// currently created. 'devices' will be empty if the kernel interactions
// were successful and there are no block devices at the moment. Returns
@@ -105,8 +157,28 @@
// Returns the path to the device mapper device node in '/dev' corresponding to
// 'name'. If the device does not exist, false is returned, and the path
// parameter is not set.
+ //
+ // This returns a path in the format "/dev/block/dm-N" that can be easily
+ // re-used with sysfs.
+ //
+ // WaitForFile() should not be used in conjunction with this call, since it
+ // could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns a device's unique path as generated by ueventd. This will return
+ // true as long as the device has been created, even if ueventd has not
+ // processed it yet.
+ //
+ // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.
+ bool GetDeviceUniquePath(const std::string& name, std::string* path);
+
+ // Returns the dev_t for the named device-mapper node.
+ bool GetDeviceNumber(const std::string& name, dev_t* dev);
+
+ // Returns a major:minor string for the named device-mapper node, that can
+ // be used as inputs to DmTargets that take a block device.
+ bool GetDeviceString(const std::string& name, std::string* dev);
+
// The only way to create a DeviceMapper object.
static DeviceMapper& Instance();
@@ -128,6 +200,12 @@
};
bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);
+ // Identical to GetTableStatus, except also retrives the active table for the device
+ // mapper device from the kernel.
+ bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
+
+ static std::string GetTargetType(const struct dm_target_spec& spec);
+
private:
// Maximum possible device mapper targets registered in the kernel.
// This is only used to read the list of targets from kernel so we allocate
@@ -140,16 +218,12 @@
// limit we are imposing here of 256.
static constexpr uint32_t kMaxPossibleDmDevices = 256;
+ bool CreateDevice(const std::string& name, const std::string& uuid = {});
+ bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
DeviceMapper();
- // Creates a device mapper device with given name.
- // Return 'true' on success and 'false' on failure to
- // create OR if a device mapper device with the same name already
- // exists.
- bool CreateDevice(const std::string& name);
-
int fd_;
// Non-copyable & Non-movable
DeviceMapper(const DeviceMapper&) = delete;
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
index 5c639be..ee66653 100644
--- a/fs_mgr/libdm/include/libdm/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -43,12 +43,20 @@
// successfully removed.
bool RemoveTarget(std::unique_ptr<DmTarget>&& target);
+ // Adds a target, constructing it in-place for convenience. For example,
+ //
+ // table.Emplace<DmTargetZero>(0, num_sectors);
+ template <typename T, typename... Args>
+ bool Emplace(Args&&... args) {
+ return AddTarget(std::make_unique<T>(std::forward<Args>(args)...));
+ }
+
// Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps
// and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the
// table is malformed.
bool valid() const;
- // Returns the toatl number of targets.
+ // Returns the total number of targets.
size_t num_targets() const { return targets_.size(); }
// Returns the total size represented by the table in terms of number of 512-byte sectors.
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 175b0f0..ab7c2db 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,6 +40,18 @@
return std::to_string(major_) + "." + std::to_string(minor_) + "." + std::to_string(patch_);
}
+ uint32_t major_version() const { return major_; }
+ uint32_t minor_version() const { return minor_; }
+ uint32_t patch_level() const { return patch_; }
+
+ bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {
+ if (major_ > major) return true;
+ if (major_ < major) return false;
+ if (minor_ > minor) return true;
+ if (minor_ < minor) return false;
+ return patch_ >= patch;
+ }
+
private:
std::string name_;
uint32_t major_;
@@ -170,6 +182,120 @@
std::string target_string_;
};
+enum class SnapshotStorageMode {
+ // The snapshot will be persisted to the COW device.
+ Persistent,
+ // The snapshot will be lost on reboot.
+ Transient,
+ // The snapshot will be merged from the COW device into the base device,
+ // in the background.
+ Merge
+};
+
+// Writes to a snapshot device will be written to the given COW device. Reads
+// will read from the COW device or base device. The chunk size is specified
+// in sectors.
+class DmTargetSnapshot final : public DmTarget {
+ public:
+ DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,
+ const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)
+ : DmTarget(start, length),
+ base_device_(base_device),
+ cow_device_(cow_device),
+ mode_(mode),
+ chunk_size_(chunk_size) {}
+
+ std::string name() const override;
+ std::string GetParameterString() const override;
+ bool Valid() const override { return true; }
+
+ struct Status {
+ uint64_t sectors_allocated;
+ uint64_t total_sectors;
+ uint64_t metadata_sectors;
+ std::string error;
+ };
+
+ static double MergePercent(const Status& status, uint64_t sectors_initial = 0);
+ static bool ParseStatusText(const std::string& text, Status* status);
+ static bool ReportsOverflow(const std::string& target_type);
+
+ private:
+ std::string base_device_;
+ std::string cow_device_;
+ SnapshotStorageMode mode_;
+ uint64_t chunk_size_;
+};
+
+// snapshot-origin will read/write directly to the backing device, updating any
+// snapshot devices with a matching origin.
+class DmTargetSnapshotOrigin final : public DmTarget {
+ public:
+ DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)
+ : DmTarget(start, length), device_(device) {}
+
+ std::string name() const override { return "snapshot-origin"; }
+ std::string GetParameterString() const override { return device_; }
+ bool Valid() const override { return true; }
+
+ private:
+ std::string device_;
+};
+
+class DmTargetCrypt final : public DmTarget {
+ public:
+ DmTargetCrypt(uint64_t start, uint64_t length, const std::string& cipher,
+ const std::string& key, uint64_t iv_sector_offset, const std::string& device,
+ uint64_t device_sector)
+ : DmTarget(start, length),
+ cipher_(cipher),
+ key_(key),
+ iv_sector_offset_(iv_sector_offset),
+ device_(device),
+ device_sector_(device_sector) {}
+
+ void AllowDiscards() { allow_discards_ = true; }
+ void AllowEncryptOverride() { allow_encrypt_override_ = true; }
+ void SetIvLargeSectors() { iv_large_sectors_ = true; }
+ void SetSectorSize(uint32_t sector_size) { sector_size_ = sector_size; }
+
+ std::string name() const override { return "crypt"; }
+ bool Valid() const override { return true; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string cipher_;
+ std::string key_;
+ uint64_t iv_sector_offset_;
+ std::string device_;
+ uint64_t device_sector_;
+ bool allow_discards_ = false;
+ bool allow_encrypt_override_ = false;
+ bool iv_large_sectors_ = false;
+ uint32_t sector_size_ = 0;
+};
+
+class DmTargetDefaultKey final : public DmTarget {
+ public:
+ DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,
+ const std::string& key, const std::string& blockdev, uint64_t start_sector)
+ : DmTarget(start, length),
+ cipher_(cipher),
+ key_(key),
+ blockdev_(blockdev),
+ start_sector_(start_sector) {}
+
+ std::string name() const override { return "default-key"; }
+ bool Valid() const override { return true; }
+ std::string GetParameterString() const override;
+
+ private:
+ std::string cipher_;
+ std::string key_;
+ std::string blockdev_;
+ uint64_t start_sector_;
+};
+
} // namespace dm
} // namespace android
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index e6e83f4..eeed6b5 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -17,6 +17,7 @@
#ifndef _LIBDM_LOOP_CONTROL_H_
#define _LIBDM_LOOP_CONTROL_H_
+#include <chrono>
#include <string>
#include <android-base/unique_fd.h>
@@ -29,12 +30,22 @@
LoopControl();
// Attaches the file specified by 'file_fd' to the loop device specified
- // by 'loopdev'
- bool Attach(int file_fd, std::string* loopdev) const;
+ // by 'loopdev'. It is possible that in between allocating and attaching
+ // a loop device, another process attaches to the chosen loop device. If
+ // this happens, Attach() will retry for up to |timeout_ms|. The timeout
+ // should not be zero.
+ //
+ // The caller does not have to call WaitForFile(); it is implicitly called.
+ // The given |timeout_ms| covers both potential sources of timeout.
+ bool Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+ std::string* loopdev) const;
// Detach the loop device given by 'loopdev' from the attached backing file.
bool Detach(const std::string& loopdev) const;
+ // Enable Direct I/O on a loop device. This requires kernel 4.9+.
+ static bool EnableDirectIo(int fd);
+
LoopControl(const LoopControl&) = delete;
LoopControl& operator=(const LoopControl&) = delete;
LoopControl& operator=(LoopControl&&) = default;
@@ -53,13 +64,13 @@
public:
// Create a loop device for the given file descriptor. It is closed when
// LoopDevice is destroyed only if auto_close is true.
- LoopDevice(int fd, bool auto_close = false);
+ LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close = false);
// Create a loop device for the given file path. It will be opened for
// reading and writing and closed when the loop device is detached.
- explicit LoopDevice(const std::string& path);
+ LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms);
~LoopDevice();
- bool valid() const { return fd_ != -1 && !device_.empty(); }
+ bool valid() const { return valid_; }
const std::string& device() const { return device_; }
LoopDevice(const LoopDevice&) = delete;
@@ -68,12 +79,13 @@
LoopDevice(LoopDevice&&) = default;
private:
- void Init();
+ void Init(const std::chrono::milliseconds& timeout_ms);
android::base::unique_fd fd_;
bool owns_fd_;
std::string device_;
LoopControl control_;
+ bool valid_ = false;
};
} // namespace dm
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 0beb1a6..edc9a45 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -27,6 +27,8 @@
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include "utility.h"
+
namespace android {
namespace dm {
@@ -37,21 +39,40 @@
}
}
-bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
- if (!FindFreeLoopDevice(loopdev)) {
- LOG(ERROR) << "Failed to attach, no free loop devices";
- return false;
- }
+bool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+ std::string* loopdev) const {
+ auto start_time = std::chrono::steady_clock::now();
+ auto condition = [&]() -> WaitResult {
+ if (!FindFreeLoopDevice(loopdev)) {
+ LOG(ERROR) << "Failed to attach, no free loop devices";
+ return WaitResult::Fail;
+ }
- android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
- if (loop_fd < 0) {
- PLOG(ERROR) << "Failed to open: " << *loopdev;
- return false;
- }
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) {
+ LOG(ERROR) << "Timed out waiting for path: " << *loopdev;
+ return WaitResult::Fail;
+ }
- int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
- if (rc < 0) {
- PLOG(ERROR) << "Failed LOOP_SET_FD";
+ android::base::unique_fd loop_fd(
+ TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop_fd < 0) {
+ PLOG(ERROR) << "Failed to open: " << *loopdev;
+ return WaitResult::Fail;
+ }
+
+ if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) {
+ return WaitResult::Done;
+ }
+ if (errno != EBUSY) {
+ PLOG(ERROR) << "Failed LOOP_SET_FD";
+ return WaitResult::Fail;
+ }
+ return WaitResult::Wait;
+ };
+ if (!WaitForCondition(condition, timeout_ms)) {
+ LOG(ERROR) << "Timed out trying to acquire a loop device";
return false;
}
return true;
@@ -91,17 +112,40 @@
return true;
}
-LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
- Init();
+bool LoopControl::EnableDirectIo(int fd) {
+#if !defined(LOOP_SET_BLOCK_SIZE)
+ static constexpr int LOOP_SET_BLOCK_SIZE = 0x4C09;
+#endif
+#if !defined(LOOP_SET_DIRECT_IO)
+ static constexpr int LOOP_SET_DIRECT_IO = 0x4C08;
+#endif
+
+ // Note: the block size has to be >= the logical block size of the underlying
+ // block device, *not* the filesystem block size.
+ if (ioctl(fd, LOOP_SET_BLOCK_SIZE, 4096)) {
+ PLOG(ERROR) << "Could not set loop device block size";
+ return false;
+ }
+ if (ioctl(fd, LOOP_SET_DIRECT_IO, 1)) {
+ PLOG(ERROR) << "Could not set loop direct IO";
+ return false;
+ }
+ return true;
}
-LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+LoopDevice::LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close)
+ : fd_(fd), owns_fd_(auto_close) {
+ Init(timeout_ms);
+}
+
+LoopDevice::LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms)
+ : fd_(-1), owns_fd_(true) {
fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
if (fd_ < -1) {
PLOG(ERROR) << "open failed for " << path;
return;
}
- Init();
+ Init(timeout_ms);
}
LoopDevice::~LoopDevice() {
@@ -113,8 +157,8 @@
}
}
-void LoopDevice::Init() {
- control_.Attach(fd_, &device_);
+void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {
+ valid_ = control_.Attach(fd_, timeout_ms, &device_);
}
} // namespace dm
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
index 08bdc00..0749f26 100644
--- a/fs_mgr/libdm/loop_control_test.cpp
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -53,7 +53,7 @@
unique_fd fd = TempFile();
ASSERT_GE(fd, 0);
- LoopDevice loop(fd);
+ LoopDevice loop(fd, 10s);
ASSERT_TRUE(loop.valid());
char buffer[6];
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
new file mode 100644
index 0000000..eccf2fb
--- /dev/null
+++ b/fs_mgr/libdm/utility.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "utility.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <thread>
+
+using namespace std::literals;
+
+namespace android {
+namespace dm {
+
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+ const std::chrono::milliseconds& timeout_ms) {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ auto result = condition();
+ if (result == WaitResult::Done) return true;
+ if (result == WaitResult::Fail) return false;
+
+ std::this_thread::sleep_for(20ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > timeout_ms) return false;
+ }
+}
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
+ auto condition = [&]() -> WaitResult {
+ // If the file exists but returns EPERM or something, we consider the
+ // condition met.
+ if (access(path.c_str(), F_OK) != 0) {
+ if (errno == ENOENT) return WaitResult::Wait;
+ }
+ return WaitResult::Done;
+ };
+ return WaitForCondition(condition, timeout_ms);
+}
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
new file mode 100644
index 0000000..f1dce9e
--- /dev/null
+++ b/fs_mgr/libdm/utility.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+namespace android {
+namespace dm {
+
+enum class WaitResult { Wait, Done, Fail };
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+ const std::chrono::milliseconds& timeout_ms);
+
+} // namespace dm
+} // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
new file mode 100644
index 0000000..414a186
--- /dev/null
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -0,0 +1,144 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libfs_avb",
+ defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
+ host_supported: true,
+ export_include_dirs: ["include"],
+ srcs: [
+ "avb_ops.cpp",
+ "avb_util.cpp",
+ "fs_avb.cpp",
+ "fs_avb_util.cpp",
+ "types.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libavb",
+ "libdm",
+ "libfstab",
+ ],
+ export_static_lib_headers: [
+ "libfstab",
+ ],
+ shared_libs: [
+ "libcrypto",
+ ],
+ header_libs: [
+ "libbase_headers",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_defaults {
+ name: "libfs_avb_host_test_defaults",
+ required: [
+ "avbtool",
+ ],
+ data: [
+ "tests/data/*",
+ ],
+ static_libs: [
+ "libavb",
+ "libavb_host_sysdeps",
+ "libdm",
+ "libext2_uuid",
+ "libfs_avb",
+ "libfstab",
+ "libgtest_host",
+ ],
+ shared_libs: [
+ "libbase",
+ "libchrome",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ cflags: [
+ "-DHOST_TEST",
+ ],
+}
+
+cc_library_host_static {
+ name: "libfs_avb_test_util",
+ defaults: ["libfs_avb_host_test_defaults"],
+ srcs: [
+ "tests/fs_avb_test_util.cpp",
+ ],
+}
+
+cc_test_host {
+ name: "libfs_avb_test",
+ defaults: ["libfs_avb_host_test_defaults"],
+ test_suites: ["general-tests"],
+ static_libs: [
+ "libfs_avb_test_util",
+ ],
+ shared_libs: [
+ "libcrypto",
+ ],
+ srcs: [
+ "tests/basic_test.cpp",
+ "tests/fs_avb_test.cpp",
+ "tests/fs_avb_util_test.cpp",
+ ],
+}
+
+cc_test_host {
+ name: "libfs_avb_internal_test",
+ defaults: ["libfs_avb_host_test_defaults"],
+ test_suites: ["general-tests"],
+ static_libs: [
+ "libfs_avb_test_util",
+ ],
+ srcs: [
+ "avb_util.cpp",
+ "util.cpp",
+ "tests/avb_util_test.cpp",
+ "tests/util_test.cpp",
+ ],
+}
+
+cc_test {
+ name: "libfs_avb_device_test",
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libavb",
+ "libdm",
+ "libfs_avb",
+ "libfstab",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ ],
+ srcs: [
+ "tests/fs_avb_device_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/fs_mgr/libfs_avb/TEST_MAPPING b/fs_mgr/libfs_avb/TEST_MAPPING
new file mode 100644
index 0000000..b0f36d4
--- /dev/null
+++ b/fs_mgr/libfs_avb/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "libfs_avb_test",
+ "host": true
+ },
+ {
+ "name": "libfs_avb_internal_test",
+ "host": true
+ }
+ ]
+}
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
similarity index 63%
rename from fs_mgr/fs_mgr_avb_ops.cpp
rename to fs_mgr/libfs_avb/avb_ops.cpp
index 43879fe..c192bf5 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -22,28 +22,34 @@
* SOFTWARE.
*/
-#include "fs_mgr_priv_avb_ops.h"
+#include "avb_ops.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
+
#include <string>
#include <android-base/macros.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <libavb/libavb.h>
+#include <libdm/dm.h>
#include <utils/Compat.h>
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
+#include "util.h"
+
+using namespace std::literals;
+
+namespace android {
+namespace fs_mgr {
static AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,
size_t num_bytes, void* buffer, size_t* out_num_read) {
return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(
- partition, offset, num_bytes, buffer, out_num_read);
+ partition, offset, num_bytes, buffer, out_num_read);
}
static AvbIOResult dummy_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,
@@ -56,9 +62,10 @@
}
static AvbIOResult dummy_validate_vbmeta_public_key(
- AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
- size_t public_key_length ATTRIBUTE_UNUSED, const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
- size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
+ AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,
+ size_t public_key_length ATTRIBUTE_UNUSED,
+ const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,
+ size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {
// vbmeta public key has been checked in bootloader phase.
// In user-space, returns true to pass the check.
//
@@ -98,7 +105,21 @@
return AVB_IO_RESULT_OK;
}
-void FsManagerAvbOps::InitializeAvbOps() {
+// Converts a partition name (with ab_suffix) to the corresponding mount point.
+// e.g., "system_a" => "/system",
+// e.g., "vendor_a" => "/vendor",
+static std::string DeriveMountPoint(const std::string& partition_name) {
+ const std::string ab_suffix = fs_mgr_get_slot_suffix();
+ std::string mount_point(partition_name);
+ auto found = partition_name.rfind(ab_suffix);
+ if (found != std::string::npos) {
+ mount_point.erase(found); // converts system_a => system
+ }
+
+ return "/" + mount_point;
+}
+
+FsManagerAvbOps::FsManagerAvbOps() {
// We only need to provide the implementation of read_from_partition()
// operation since that's all what is being used by the avb_slot_verify().
// Other I/O operations are only required in bootloader but not in
@@ -116,35 +137,53 @@
avb_ops_.user_data = this;
}
-FsManagerAvbOps::FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map)
- : by_name_symlink_map_(std::move(by_name_symlink_map)) {
- InitializeAvbOps();
-}
-
-FsManagerAvbOps::FsManagerAvbOps(const fstab& fstab) {
- // Constructs the by-name symlink map for each fstab record.
- // /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a =>
- // by_name_symlink_map_["system_a"] = "/dev/block/platform/soc.0/7824900.sdhci/by-name/system_a"
- for (int i = 0; i < fstab.num_entries; i++) {
- std::string partition_name = basename(fstab.recs[i].blk_device);
- by_name_symlink_map_[partition_name] = fstab.recs[i].blk_device;
+// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
+// dm-linear path for it. e.g., /dev/block/dm-0. If not found, returns an empty string.
+// This assumes that the prefix of the partition name and the mount point are the same.
+// e.g., partition vendor_a is mounted under /vendor, product_a is mounted under /product, etc.
+// This might not be true for some special fstab files, e.g., fstab.postinstall.
+// But it's good enough for the default fstab. Also note that the logical path is a
+// fallback solution when the physical path (/dev/block/by-name/<partition>) cannot be found.
+std::string FsManagerAvbOps::GetLogicalPath(const std::string& partition_name) {
+ if (fstab_.empty() && !ReadDefaultFstab(&fstab_)) {
+ return "";
}
- InitializeAvbOps();
+
+ const auto mount_point = DeriveMountPoint(partition_name);
+ if (mount_point.empty()) return "";
+
+ auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
+ if (!fstab_entry) return "";
+
+ std::string device_path;
+ if (fstab_entry->fs_mgr_flags.logical) {
+ dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+ if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {
+ LERROR << "Failed to resolve logical device path for: " << fstab_entry->blk_device;
+ return "";
+ }
+ return device_path;
+ }
+
+ return "";
}
AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
size_t num_bytes, void* buffer,
size_t* out_num_read) {
- const auto iter = by_name_symlink_map_.find(partition);
- if (iter == by_name_symlink_map_.end()) {
- LERROR << "by-name symlink not found for partition: '" << partition << "'";
- return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
- }
- std::string path = iter->second;
+ std::string path = "/dev/block/by-name/"s + partition;
// Ensures the device path (a symlink created by init) is ready to access.
- if (!fs_mgr_wait_for_file(path, 1s)) {
- return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ if (!WaitForFile(path, 1s)) {
+ LERROR << "Device path not found: " << path;
+ // Falls back to logical path if the physical path is not found.
+ // This mostly only works for emulator (no bootloader). Because in normal
+ // device, bootloader is unable to read logical partitions. So if libavb in
+ // the bootloader failed to read a physical partition, it will failed to boot
+ // the HLOS and we won't reach the code here.
+ path = GetLogicalPath(partition);
+ if (path.empty() || !WaitForFile(path, 1s)) return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+ LINFO << "Fallback to use logical device path: " << path;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
@@ -186,14 +225,35 @@
AvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,
AvbSlotVerifyFlags flags,
- AvbSlotVerifyData** out_data) {
+ std::vector<VBMetaData>* out_vbmeta_images) {
// Invokes avb_slot_verify() to load and verify all vbmeta images.
// Sets requested_partitions to nullptr as it's to copy the contents
// of HASH partitions into handle>avb_slot_data_, which is not required as
// fs_mgr only deals with HASHTREE partitions.
const char* requested_partitions[] = {nullptr};
+
+ // Local resource to store vbmeta images from avb_slot_verify();
+ AvbSlotVerifyData* avb_slot_data;
+
// The |hashtree_error_mode| field doesn't matter as it only
// influences the generated kernel cmdline parameters.
- return avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
- AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, out_data);
+ auto verify_result =
+ avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,
+ AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data);
+
+ if (!avb_slot_data) return verify_result;
+ // Copies avb_slot_data->vbmeta_images[].
+ for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
+ out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,
+ avb_slot_data->vbmeta_images[i].vbmeta_size,
+ avb_slot_data->vbmeta_images[i].partition_name));
+ }
+
+ // Free the local resource.
+ avb_slot_verify_data_free(avb_slot_data);
+
+ return verify_result;
}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_priv_avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
similarity index 82%
rename from fs_mgr/fs_mgr_priv_avb_ops.h
rename to fs_mgr/libfs_avb/avb_ops.h
index d1ef2e9..b39812d 100644
--- a/fs_mgr/fs_mgr_priv_avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -22,15 +22,17 @@
* SOFTWARE.
*/
-#ifndef __CORE_FS_MGR_PRIV_AVB_OPS_H
-#define __CORE_FS_MGR_PRIV_AVB_OPS_H
+#pragma once
-#include <map>
#include <string>
+#include <vector>
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
#include <libavb/libavb.h>
-#include "fs_mgr.h"
+namespace android {
+namespace fs_mgr {
// This class provides C++ bindings to interact with libavb, a small
// self-contained piece of code that's intended to be used in bootloaders.
@@ -42,12 +44,11 @@
// read and verify the metadata and store it into the out_data parameter.
// The caller MUST check the integrity of metadata against the
// androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.
-// e.g., see class FsManagerAvbVerifier for more details.
+// e.g., see class AvbVerifier for more details.
//
class FsManagerAvbOps {
public:
- FsManagerAvbOps(const fstab& fstab);
- FsManagerAvbOps(std::map<std::string, std::string>&& by_name_symlink_map);
+ FsManagerAvbOps();
static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -57,12 +58,13 @@
void* buffer, size_t* out_num_read);
AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
- AvbSlotVerifyData** out_data);
+ std::vector<VBMetaData>* out_vbmeta_images);
private:
- void InitializeAvbOps();
-
+ std::string GetLogicalPath(const std::string& partition_name);
AvbOps avb_ops_;
- std::map<std::string, std::string> by_name_symlink_map_;
+ Fstab fstab_;
};
-#endif /* __CORE_FS_MGR_PRIV_AVB_OPS_H */
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
new file mode 100644
index 0000000..4505382
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "avb_util.h"
+
+#include <unistd.h>
+
+#include <array>
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::Basename;
+using android::base::ReadFileToString;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images) {
+ size_t value_size;
+ for (const auto& vbmeta : vbmeta_images) {
+ const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+ key.size(), &value_size);
+ if (value != nullptr) {
+ return {value, value_size};
+ }
+ }
+ return "";
+}
+
+// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
+// See the following link for more details:
+// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+ const std::string& blk_device, android::dm::DmTable* table) {
+ // Loads androidboot.veritymode from kernel cmdline.
+ std::string verity_mode;
+ if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+ verity_mode = "enforcing"; // Defaults to enforcing when it's absent.
+ }
+
+ // Converts veritymode to the format used in kernel.
+ std::string dm_verity_mode;
+ if (verity_mode == "enforcing") {
+ dm_verity_mode = "restart_on_corruption";
+ } else if (verity_mode == "logging") {
+ dm_verity_mode = "ignore_corruption";
+ } else if (verity_mode != "eio") { // Default dm_verity_mode is eio.
+ LERROR << "Unknown androidboot.veritymode: " << verity_mode;
+ return false;
+ }
+
+ std::ostringstream hash_algorithm;
+ hash_algorithm << hashtree_desc.hash_algorithm;
+
+ android::dm::DmTargetVerity target(
+ 0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,
+ blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
+ hashtree_desc.image_size / hashtree_desc.data_block_size,
+ hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),
+ hashtree_desc.root_digest, hashtree_desc.salt);
+ if (hashtree_desc.fec_size > 0) {
+ target.UseFec(blk_device, hashtree_desc.fec_num_roots,
+ hashtree_desc.fec_offset / hashtree_desc.data_block_size,
+ hashtree_desc.fec_offset / hashtree_desc.data_block_size);
+ }
+ if (!dm_verity_mode.empty()) {
+ target.SetVerityMode(dm_verity_mode);
+ }
+ // Always use ignore_zero_blocks.
+ target.IgnoreZeroBlocks();
+
+ LINFO << "Built verity table: '" << target.GetParameterString() << "'";
+
+ return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
+}
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
+ bool wait_for_verity_dev) {
+ android::dm::DmTable table;
+ if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {
+ LERROR << "Failed to construct verity table.";
+ return false;
+ }
+ table.set_readonly(true);
+
+ std::chrono::milliseconds timeout = {};
+ if (wait_for_verity_dev) timeout = 1s;
+
+ std::string dev_path;
+ const std::string mount_point(Basename(fstab_entry->mount_point));
+ const std::string device_name(GetVerityDeviceName(*fstab_entry));
+ android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+ if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
+ LERROR << "Couldn't create verity device!";
+ return false;
+ }
+
+ // Marks the underlying block device as read-only.
+ SetBlockDeviceReadOnly(fstab_entry->blk_device);
+
+ // Updates fstab_rec->blk_device to verity device name.
+ fstab_entry->blk_device = dev_path;
+ return true;
+}
+
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+ bool found = false;
+ const uint8_t* desc_partition_name;
+ auto hashtree_desc = std::make_unique<FsAvbHashtreeDescriptor>();
+
+ for (const auto& vbmeta : vbmeta_images) {
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ continue;
+ }
+
+ for (size_t n = 0; n < num_descriptors && !found; n++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+ LWARNING << "Descriptor[" << n << "] is invalid";
+ continue;
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {
+ desc_partition_name =
+ (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor);
+ if (!avb_hashtree_descriptor_validate_and_byteswap(
+ (AvbHashtreeDescriptor*)descriptors[n], hashtree_desc.get())) {
+ continue;
+ }
+ if (hashtree_desc->partition_name_len != partition_name.length()) {
+ continue;
+ }
+ // Notes that desc_partition_name is not NUL-terminated.
+ std::string hashtree_partition_name((const char*)desc_partition_name,
+ hashtree_desc->partition_name_len);
+ if (hashtree_partition_name == partition_name) {
+ found = true;
+ }
+ }
+ }
+
+ if (found) break;
+ }
+
+ if (!found) {
+ LERROR << "Hashtree descriptor not found: " << partition_name;
+ return nullptr;
+ }
+
+ hashtree_desc->partition_name = partition_name;
+
+ const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;
+ hashtree_desc->salt = BytesToHex(desc_salt, hashtree_desc->salt_len);
+
+ const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;
+ hashtree_desc->root_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);
+
+ return hashtree_desc;
+}
+
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+ const std::vector<VBMetaData>& vbmeta_images,
+ const std::string& ab_suffix,
+ const std::string& ab_other_suffix) {
+ // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+ // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+ std::string partition_name = DeriveAvbPartitionName(*fstab_entry, ab_suffix, ab_other_suffix);
+
+ if (partition_name.empty()) {
+ LERROR << "partition name is empty, cannot lookup AVB descriptors";
+ return false;
+ }
+
+ std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
+ GetHashtreeDescriptor(partition_name, vbmeta_images);
+ if (!hashtree_descriptor) {
+ return false;
+ }
+
+ // Converts HASHTREE descriptor to verity table to load into kernel.
+ // When success, the new device path will be returned, e.g., /dev/block/dm-2.
+ return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, wait_for_verity_dev);
+}
+
+// Converts a AVB partition_name (without A/B suffix) to a device partition name.
+// e.g., "system" => "system_a",
+// "system_other" => "system_b".
+//
+// If the device is non-A/B, converts it to a partition name without suffix.
+// e.g., "system" => "system",
+// "system_other" => "system".
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+ const std::string& ab_suffix,
+ const std::string& ab_other_suffix) {
+ bool is_other_slot = false;
+ std::string sanitized_partition_name(avb_partition_name);
+
+ auto other_suffix = sanitized_partition_name.rfind("_other");
+ if (other_suffix != std::string::npos) {
+ sanitized_partition_name.erase(other_suffix); // converts system_other => system
+ is_other_slot = true;
+ }
+
+ auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;
+ return sanitized_partition_name + append_suffix;
+}
+
+// Converts fstab_entry.blk_device (with ab_suffix) to a AVB partition name.
+// e.g., "/dev/block/by-name/system_a", slot_select => "system",
+// "/dev/block/by-name/system_b", slot_select_other => "system_other".
+//
+// Or for a logical partition (with ab_suffix):
+// e.g., "system_a", slot_select => "system",
+// "system_b", slot_select_other => "system_other".
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+ const std::string& ab_other_suffix) {
+ std::string partition_name;
+ if (fstab_entry.fs_mgr_flags.logical) {
+ partition_name = fstab_entry.logical_partition_name;
+ } else {
+ partition_name = Basename(fstab_entry.blk_device);
+ }
+
+ if (fstab_entry.fs_mgr_flags.slot_select) {
+ auto found = partition_name.rfind(ab_suffix);
+ if (found != std::string::npos) {
+ partition_name.erase(found); // converts system_a => system
+ }
+ } else if (fstab_entry.fs_mgr_flags.slot_select_other) {
+ auto found = partition_name.rfind(ab_other_suffix);
+ if (found != std::string::npos) {
+ partition_name.erase(found); // converts system_b => system
+ }
+ partition_name += "_other"; // converts system => system_other
+ }
+
+ return partition_name;
+}
+
+off64_t GetTotalSize(int fd) {
+ off64_t saved_current = lseek64(fd, 0, SEEK_CUR);
+ if (saved_current == -1) {
+ PERROR << "Failed to get current position";
+ return -1;
+ }
+
+ // lseek64() returns the resulting offset location from the beginning of the file.
+ off64_t total_size = lseek64(fd, 0, SEEK_END);
+ if (total_size == -1) {
+ PERROR << "Failed to lseek64 to end of the partition";
+ return -1;
+ }
+
+ // Restores the original offset.
+ if (lseek64(fd, saved_current, SEEK_SET) == -1) {
+ PERROR << "Failed to lseek64 to the original offset: " << saved_current;
+ }
+
+ return total_size;
+}
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd) {
+ std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;
+ auto footer = std::make_unique<AvbFooter>();
+
+ off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;
+
+ ssize_t num_read =
+ TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));
+ if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {
+ PERROR << "Failed to read AVB footer at offset: " << footer_offset;
+ return nullptr;
+ }
+
+ if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {
+ PERROR << "AVB footer verification failed at offset " << footer_offset;
+ return nullptr;
+ }
+
+ return footer;
+}
+
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length,
+ const std::string& expected_key_blob) {
+ if (expected_key_blob.empty()) { // no expectation of the key, return true.
+ return true;
+ }
+ if (expected_key_blob.size() != length) {
+ return false;
+ }
+ if (0 == memcmp(key, expected_key_blob.data(), length)) {
+ return true;
+ }
+ return false;
+}
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+ const std::vector<std::string>& allowed_key_paths) {
+ std::string allowed_key_blob;
+ if (key_blob_to_validate.empty()) {
+ LWARNING << "Failed to validate an empty key";
+ return false;
+ }
+ for (const auto& path : allowed_key_paths) {
+ if (ReadFileToString(path, &allowed_key_blob)) {
+ if (key_blob_to_validate == allowed_key_blob) return true;
+ }
+ }
+ return false;
+}
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data) {
+ const uint8_t* pk_data;
+ size_t pk_len;
+ ::AvbVBMetaVerifyResult vbmeta_ret;
+
+ vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);
+
+ if (out_public_key_data != nullptr) {
+ out_public_key_data->clear();
+ if (pk_len > 0) {
+ out_public_key_data->append(reinterpret_cast<const char*>(pk_data), pk_len);
+ }
+ }
+
+ switch (vbmeta_ret) {
+ case AVB_VBMETA_VERIFY_RESULT_OK:
+ if (pk_data == nullptr || pk_len <= 0) {
+ LERROR << vbmeta.partition()
+ << ": Error verifying vbmeta image: failed to get public key";
+ return VBMetaVerifyResult::kError;
+ }
+ if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: public key used to"
+ << " sign data does not match key in chain descriptor";
+ return VBMetaVerifyResult::kErrorVerification;
+ }
+ return VBMetaVerifyResult::kSuccess;
+ case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:
+ case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:
+ case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: "
+ << avb_vbmeta_verify_result_to_string(vbmeta_ret);
+ return VBMetaVerifyResult::kErrorVerification;
+ case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:
+ // No way to continue this case.
+ LERROR << vbmeta.partition() << ": Error verifying vbmeta image: invalid vbmeta header";
+ break;
+ case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:
+ // No way to continue this case.
+ LERROR << vbmeta.partition()
+ << ": Error verifying vbmeta image: unsupported AVB version";
+ break;
+ default:
+ LERROR << "Unknown vbmeta image verify return value: " << vbmeta_ret;
+ break;
+ }
+
+ return VBMetaVerifyResult::kError;
+}
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ VBMetaVerifyResult* out_verify_result) {
+ uint64_t vbmeta_offset = 0;
+ uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;
+ bool is_vbmeta_partition = StartsWith(partition_name, "vbmeta");
+
+ if (out_verify_result) {
+ *out_verify_result = VBMetaVerifyResult::kError;
+ }
+
+ if (!is_vbmeta_partition) {
+ std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+ if (!footer) {
+ return nullptr;
+ }
+ vbmeta_offset = footer->vbmeta_offset;
+ vbmeta_size = footer->vbmeta_size;
+ }
+
+ if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {
+ LERROR << "VbMeta size in footer exceeds kMaxVBMetaSize";
+ return nullptr;
+ }
+
+ auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);
+ ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));
+ // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.
+ if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {
+ PERROR << partition_name << ": Failed to read vbmeta at offset " << vbmeta_offset
+ << " with size " << vbmeta_size;
+ return nullptr;
+ }
+
+ auto verify_result =
+ VerifyVBMetaSignature(*vbmeta, expected_public_key_blob, out_public_key_data);
+
+ if (out_verify_result != nullptr) {
+ *out_verify_result = verify_result;
+ }
+
+ if (verify_result == VBMetaVerifyResult::kSuccess ||
+ verify_result == VBMetaVerifyResult::kErrorVerification) {
+ return vbmeta;
+ }
+
+ return nullptr;
+}
+
+bool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,
+ uint64_t rollback_index ATTRIBUTE_UNUSED) {
+ // TODO(bowgotsai): Support rollback protection.
+ return false;
+}
+
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {
+ CHECK(fatal_error != nullptr);
+ std::vector<ChainInfo> chain_partitions;
+
+ size_t num_descriptors;
+ std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(
+ avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+ if (!descriptors || num_descriptors < 1) {
+ return {};
+ }
+
+ for (size_t i = 0; i < num_descriptors; i++) {
+ AvbDescriptor desc;
+ if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {
+ LERROR << "Descriptor[" << i << "] is invalid in vbmeta: " << vbmeta.partition();
+ *fatal_error = true;
+ return {};
+ }
+ if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {
+ AvbChainPartitionDescriptor chain_desc;
+ if (!avb_chain_partition_descriptor_validate_and_byteswap(
+ (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {
+ LERROR << "Chain descriptor[" << i
+ << "] is invalid in vbmeta: " << vbmeta.partition();
+ *fatal_error = true;
+ return {};
+ }
+ const char* chain_partition_name =
+ ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);
+ const char* chain_public_key_blob =
+ chain_partition_name + chain_desc.partition_name_len;
+ chain_partitions.emplace_back(
+ std::string(chain_partition_name, chain_desc.partition_name_len),
+ std::string(chain_public_key_blob, chain_desc.public_key_len));
+ }
+ }
+
+ return chain_partitions;
+}
+
+// Loads the vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+ const std::string& image_path, const std::string& partition_name,
+ const std::string& expected_public_key_blob, bool allow_verification_error,
+ bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+ bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result) {
+ if (out_verify_result) {
+ *out_verify_result = VBMetaVerifyResult::kError;
+ }
+
+ // Ensures the device path (might be a symlink created by init) is ready to access.
+ if (!WaitForFile(image_path, 1s)) {
+ PERROR << "No such path: " << image_path;
+ return nullptr;
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(image_path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PERROR << "Failed to open: " << image_path;
+ return nullptr;
+ }
+
+ VBMetaVerifyResult verify_result;
+ std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+ fd, partition_name, expected_public_key_blob, out_public_key_data, &verify_result);
+ if (!vbmeta) {
+ LERROR << partition_name << ": Failed to load vbmeta, result: " << verify_result;
+ return nullptr;
+ }
+ vbmeta->set_vbmeta_path(image_path);
+
+ if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {
+ LERROR << partition_name << ": allow verification error is not allowed";
+ return nullptr;
+ }
+
+ std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+ vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ if (!vbmeta_header) {
+ LERROR << partition_name << ": Failed to get vbmeta header";
+ return nullptr;
+ }
+
+ if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {
+ return nullptr;
+ }
+
+ // vbmeta flags can only be set by the top-level vbmeta image.
+ if (is_chained_vbmeta && vbmeta_header->flags != 0) {
+ LERROR << partition_name << ": chained vbmeta image has non-zero flags";
+ return nullptr;
+ }
+
+ // Checks if verification has been disabled by setting a bit in the image.
+ if (out_verification_disabled) {
+ if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {
+ LWARNING << "VERIFICATION_DISABLED bit is set for partition: " << partition_name;
+ *out_verification_disabled = true;
+ } else {
+ *out_verification_disabled = false;
+ }
+ }
+
+ if (out_verify_result) {
+ *out_verify_result = verify_result;
+ }
+
+ return vbmeta;
+}
+
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+ bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+ std::vector<VBMetaData>* out_vbmeta_images) {
+ auto image_path = device_path_constructor(
+ AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));
+
+ bool verification_disabled = false;
+ VBMetaVerifyResult verify_result;
+ auto vbmeta = LoadAndVerifyVbmetaByPath(image_path, partition_name, expected_public_key_blob,
+ allow_verification_error, rollback_protection,
+ is_chained_vbmeta, nullptr /* out_public_key_data */,
+ &verification_disabled, &verify_result);
+
+ if (!vbmeta) {
+ return VBMetaVerifyResult::kError;
+ }
+ if (out_vbmeta_images) {
+ out_vbmeta_images->emplace_back(std::move(*vbmeta));
+ }
+
+ // Only loads chained vbmeta if AVB verification is NOT disabled.
+ if (!verification_disabled && load_chained_vbmeta) {
+ bool fatal_error = false;
+ auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);
+ if (fatal_error) {
+ return VBMetaVerifyResult::kError;
+ }
+ for (auto& chain : chain_partitions) {
+ auto sub_ret = LoadAndVerifyVbmetaByPartition(
+ chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,
+ allow_verification_error, load_chained_vbmeta, rollback_protection,
+ device_path_constructor, true, /* is_chained_vbmeta */
+ out_vbmeta_images);
+ if (sub_ret != VBMetaVerifyResult::kSuccess) {
+ verify_result = sub_ret; // might be 'ERROR' or 'ERROR VERIFICATION'.
+ if (verify_result == VBMetaVerifyResult::kError) {
+ return verify_result; // stop here if we got an 'ERROR'.
+ }
+ }
+ }
+ }
+
+ return verify_result;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
new file mode 100644
index 0000000..09c786a
--- /dev/null
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "fs_avb/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+struct ChainInfo {
+ std::string partition_name;
+ std::string public_key_blob;
+
+ ChainInfo(const std::string& chain_partition_name, const std::string& chain_public_key_blob)
+ : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
+};
+
+std::string GetAvbPropertyDescriptor(const std::string& key,
+ const std::vector<VBMetaData>& vbmeta_images);
+
+// AvbHashtreeDescriptor to dm-verity table setup.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
+bool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,
+ const std::string& blk_device, android::dm::DmTable* table);
+
+bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
+ bool wait_for_verity_dev);
+
+// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.
+bool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,
+ const std::vector<VBMetaData>& vbmeta_images,
+ const std::string& ab_suffix, const std::string& ab_other_suffix);
+
+// Converts AVB partition name to a device partition name.
+std::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,
+ const std::string& ab_suffix,
+ const std::string& ab_other_suffix);
+
+// Converts by-name symlink to AVB partition name.
+std::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,
+ const std::string& ab_other_suffix);
+
+// AvbFooter and AvbMetaImage maninpulations.
+off64_t GetTotalSize(int fd);
+
+std::unique_ptr<AvbFooter> GetAvbFooter(int fd);
+
+std::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ VBMetaVerifyResult* out_verify_result);
+
+VBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data);
+
+bool ValidatePublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);
+
+bool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,
+ const std::vector<std::string>& expected_key_paths);
+
+// Detects if whether a partition contains a rollback image.
+bool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);
+
+// Extracts chain partition info.
+std::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);
+
+// Loads the single vbmeta from a given path.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(
+ const std::string& image_path, const std::string& partition_name,
+ const std::string& expected_public_key_blob, bool allow_verification_error,
+ bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,
+ bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);
+
+// Loads the top-level vbmeta and all its chained vbmeta images.
+// The actual device path is constructed at runtime by:
+// partition_name, ab_suffix, ab_other_suffix, and device_path_constructor.
+VBMetaVerifyResult LoadAndVerifyVbmetaByPartition(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_blob,
+ bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,
+ std::vector<VBMetaData>* out_vbmeta_images);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
new file mode 100644
index 0000000..c4d7511
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb/fs_avb.h"
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_ops.h"
+#include "avb_util.h"
+#include "sha.h"
+#include "util.h"
+
+using android::base::Basename;
+using android::base::ParseUint;
+using android::base::ReadFileToString;
+using android::base::Split;
+using android::base::StringPrintf;
+
+namespace android {
+namespace fs_mgr {
+
+template <typename Hasher>
+std::pair<size_t, bool> VerifyVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images,
+ const uint8_t* expected_digest) {
+ size_t total_size = 0;
+ Hasher hasher;
+ for (const auto& vbmeta : vbmeta_images) {
+ hasher.update(vbmeta.data(), vbmeta.size());
+ total_size += vbmeta.size();
+ }
+
+ bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);
+
+ return std::make_pair(total_size, matched);
+}
+
+template <typename Hasher>
+std::pair<std::string, size_t> CalculateVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images) {
+ std::string digest;
+ size_t total_size = 0;
+
+ Hasher hasher;
+ for (const auto& vbmeta : vbmeta_images) {
+ hasher.update(vbmeta.data(), vbmeta.size());
+ total_size += vbmeta.size();
+ }
+
+ // Converts digest bytes to a hex string.
+ digest = BytesToHex(hasher.finalize(), Hasher::DIGEST_SIZE);
+ return std::make_pair(digest, total_size);
+}
+
+// class AvbVerifier
+// -----------------
+// Reads the following values from kernel cmdline and provides the
+// VerifyVbmetaImages() to verify AvbSlotVerifyData.
+// - androidboot.vbmeta.hash_alg
+// - androidboot.vbmeta.size
+// - androidboot.vbmeta.digest
+class AvbVerifier {
+ public:
+ // The factory method to return a unique_ptr<AvbVerifier>
+ static std::unique_ptr<AvbVerifier> Create();
+ bool VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images);
+
+ protected:
+ AvbVerifier() = default;
+
+ private:
+ HashAlgorithm hash_alg_;
+ uint8_t digest_[SHA512_DIGEST_LENGTH];
+ size_t vbmeta_size_;
+};
+
+std::unique_ptr<AvbVerifier> AvbVerifier::Create() {
+ std::unique_ptr<AvbVerifier> avb_verifier(new AvbVerifier());
+ if (!avb_verifier) {
+ LERROR << "Failed to create unique_ptr<AvbVerifier>";
+ return nullptr;
+ }
+
+ std::string value;
+ if (!fs_mgr_get_boot_config("vbmeta.size", &value) ||
+ !ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {
+ LERROR << "Invalid hash size: " << value.c_str();
+ return nullptr;
+ }
+
+ // Reads hash algorithm.
+ size_t expected_digest_size = 0;
+ std::string hash_alg;
+ fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg);
+ if (hash_alg == "sha256") {
+ expected_digest_size = SHA256_DIGEST_LENGTH * 2;
+ avb_verifier->hash_alg_ = HashAlgorithm::kSHA256;
+ } else if (hash_alg == "sha512") {
+ expected_digest_size = SHA512_DIGEST_LENGTH * 2;
+ avb_verifier->hash_alg_ = HashAlgorithm::kSHA512;
+ } else {
+ LERROR << "Unknown hash algorithm: " << hash_alg.c_str();
+ return nullptr;
+ }
+
+ // Reads digest.
+ std::string digest;
+ fs_mgr_get_boot_config("vbmeta.digest", &digest);
+ if (digest.size() != expected_digest_size) {
+ LERROR << "Unexpected digest size: " << digest.size()
+ << " (expected: " << expected_digest_size << ")";
+ return nullptr;
+ }
+
+ if (!HexToBytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {
+ LERROR << "Hash digest contains non-hexidecimal character: " << digest.c_str();
+ return nullptr;
+ }
+
+ return avb_verifier;
+}
+
+bool AvbVerifier::VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images) {
+ if (vbmeta_images.empty()) {
+ LERROR << "No vbmeta images";
+ return false;
+ }
+
+ size_t total_size = 0;
+ bool digest_matched = false;
+
+ if (hash_alg_ == HashAlgorithm::kSHA256) {
+ std::tie(total_size, digest_matched) =
+ VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);
+ } else if (hash_alg_ == HashAlgorithm::kSHA512) {
+ std::tie(total_size, digest_matched) =
+ VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);
+ }
+
+ if (total_size != vbmeta_size_) {
+ LERROR << "total vbmeta size mismatch: " << total_size << " (expected: " << vbmeta_size_
+ << ")";
+ return false;
+ }
+
+ if (!digest_matched) {
+ LERROR << "vbmeta digest mismatch";
+ return false;
+ }
+
+ return true;
+}
+
+// class AvbHandle
+// ---------------
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key_path,
+ const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+ bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> custom_device_path) {
+ AvbUniquePtr avb_handle(new AvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate AvbHandle";
+ return nullptr;
+ }
+
+ std::string expected_key_blob;
+ if (!expected_public_key_path.empty()) {
+ if (access(expected_public_key_path.c_str(), F_OK) != 0) {
+ LERROR << "Expected public key path doesn't exist: " << expected_public_key_path;
+ return nullptr;
+ } else if (!ReadFileToString(expected_public_key_path, &expected_key_blob)) {
+ LERROR << "Failed to load: " << expected_public_key_path;
+ return nullptr;
+ }
+ }
+
+ auto android_by_name_symlink = [](const std::string& partition_name_with_ab) {
+ return "/dev/block/by-name/" + partition_name_with_ab;
+ };
+
+ auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;
+
+ auto verify_result = LoadAndVerifyVbmetaByPartition(
+ partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,
+ load_chained_vbmeta, rollback_protection, device_path, false,
+ /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);
+ switch (verify_result) {
+ case VBMetaVerifyResult::kSuccess:
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
+ break;
+ case VBMetaVerifyResult::kErrorVerification:
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ break;
+ default:
+ LERROR << "LoadAndVerifyVbmetaByPartition failed, result: " << verify_result;
+ return nullptr;
+ }
+
+ // Sanity check here because we have to use vbmeta_images_[0] below.
+ if (avb_handle->vbmeta_images_.size() < 1) {
+ LERROR << "LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded";
+ return nullptr;
+ }
+
+ // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+ avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+ // Checks any disabled flag is set.
+ std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =
+ avb_handle->vbmeta_images_[0].GetVBMetaHeader();
+ bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header->flags &
+ AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ bool hashtree_disabled =
+ ((AvbVBMetaImageFlags)vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (verification_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+ } else if (hashtree_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+ }
+
+ // Calculates the summary info for all vbmeta_images_;
+ std::string digest;
+ size_t total_size;
+ if (hash_algorithm == HashAlgorithm::kSHA256) {
+ std::tie(digest, total_size) =
+ CalculateVbmetaDigest<SHA256Hasher>(avb_handle->vbmeta_images_);
+ } else if (hash_algorithm == HashAlgorithm::kSHA512) {
+ std::tie(digest, total_size) =
+ CalculateVbmetaDigest<SHA512Hasher>(avb_handle->vbmeta_images_);
+ } else {
+ LERROR << "Invalid hash algorithm";
+ return nullptr;
+ }
+ avb_handle->vbmeta_info_ = VBMetaInfo(digest, hash_algorithm, total_size);
+
+ LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+ return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry) {
+ if (fstab_entry.avb_keys.empty()) {
+ LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
+ return nullptr;
+ }
+
+ // Binds allow_verification_error and rollback_protection to device unlock state.
+ bool allow_verification_error = IsDeviceUnlocked();
+ bool rollback_protection = !allow_verification_error;
+
+ std::string public_key_data;
+ bool verification_disabled = false;
+ VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;
+ std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+ fstab_entry.blk_device, "" /* partition_name, no need for a standalone path */,
+ "" /* expected_public_key_blob, */, allow_verification_error, rollback_protection,
+ false /* not is_chained_vbmeta */, &public_key_data, &verification_disabled,
+ &verify_result);
+
+ if (!vbmeta) {
+ LERROR << "Failed to load vbmeta: " << fstab_entry.blk_device;
+ return nullptr;
+ }
+
+ AvbUniquePtr avb_handle(new AvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate AvbHandle";
+ return nullptr;
+ }
+ avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));
+
+ switch (verify_result) {
+ case VBMetaVerifyResult::kSuccess:
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
+ break;
+ case VBMetaVerifyResult::kErrorVerification:
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ break;
+ default:
+ LERROR << "LoadAndVerifyVbmetaByPath failed, result: " << verify_result;
+ return nullptr;
+ }
+
+ if (!ValidatePublicKeyBlob(public_key_data, Split(fstab_entry.avb_keys, ":"))) {
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+ if (!allow_verification_error) {
+ LERROR << "Unknown public key is not allowed";
+ return nullptr;
+ }
+ }
+
+ if (verification_disabled) {
+ LINFO << "AVB verification disabled on: " << fstab_entry.mount_point;
+ avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+ }
+
+ LINFO << "Returning avb_handle for '" << fstab_entry.mount_point
+ << "' with status: " << avb_handle->status_;
+ return avb_handle;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+ // Loads inline vbmeta images, starting from /vbmeta.
+ return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+ {} /* expected_public_key, already checked by bootloader */,
+ HashAlgorithm::kSHA256,
+ IsDeviceUnlocked(), /* allow_verification_error */
+ true, /* load_chained_vbmeta */
+ false, /* rollback_protection, already checked by bootloader */
+ nullptr /* custom_device_path */);
+}
+
+// TODO(b/128807537): removes this function.
+AvbUniquePtr AvbHandle::Open() {
+ bool is_device_unlocked = IsDeviceUnlocked();
+
+ AvbUniquePtr avb_handle(new AvbHandle());
+ if (!avb_handle) {
+ LERROR << "Failed to allocate AvbHandle";
+ return nullptr;
+ }
+
+ FsManagerAvbOps avb_ops;
+ AvbSlotVerifyFlags flags = is_device_unlocked ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
+ : AVB_SLOT_VERIFY_FLAGS_NONE;
+ AvbSlotVerifyResult verify_result =
+ avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
+
+ // Only allow the following verify results:
+ // - AVB_SLOT_VERIFY_RESULT_OK.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).
+ // Might occur in either the top-level vbmeta or a chained vbmeta.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).
+ // Could only occur in a chained vbmeta. Because we have *dummy* operations in
+ // FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate
+ // the public key of the top-level vbmeta always pass in userspace here.
+ //
+ // The following verify result won't happen, because the *dummy* operation
+ // avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked
+ // vbmeta images, which should be caught in the bootloader stage, won't be detected here.
+ // - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX
+ switch (verify_result) {
+ case AVB_SLOT_VERIFY_RESULT_OK:
+ avb_handle->status_ = AvbHandleStatus::kSuccess;
+ break;
+ case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
+ case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
+ if (!is_device_unlocked) {
+ LERROR << "ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed "
+ << "if the device is LOCKED";
+ return nullptr;
+ }
+ avb_handle->status_ = AvbHandleStatus::kVerificationError;
+ break;
+ default:
+ LERROR << "avb_slot_verify failed, result: " << verify_result;
+ return nullptr;
+ }
+
+ // Sets the MAJOR.MINOR for init to set it into "ro.boot.avb_version".
+ avb_handle->avb_version_ = StringPrintf("%d.%d", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);
+
+ // Checks whether FLAGS_VERIFICATION_DISABLED is set:
+ // - Only the top-level vbmeta struct is read.
+ // - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)
+ // and AVB HASHTREE descriptor(s).
+ AvbVBMetaImageHeader vbmeta_header;
+ avb_vbmeta_image_header_to_host_byte_order(
+ (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].data(), &vbmeta_header);
+ bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+ AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+
+ if (verification_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;
+ } else {
+ // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.
+ std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();
+ if (!avb_verifier) {
+ LERROR << "Failed to create AvbVerifier";
+ return nullptr;
+ }
+ if (!avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {
+ LERROR << "VerifyVbmetaImages failed";
+ return nullptr;
+ }
+
+ // Checks whether FLAGS_HASHTREE_DISABLED is set.
+ bool hashtree_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &
+ AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ if (hashtree_disabled) {
+ avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;
+ }
+ }
+
+ LINFO << "Returning avb_handle with status: " << avb_handle->status_;
+ return avb_handle;
+}
+
+AvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
+ bool wait_for_verity_dev) {
+ auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);
+ if (!avb_handle) {
+ return AvbHashtreeResult::kFail;
+ }
+
+ return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);
+}
+
+AvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {
+ if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {
+ return AvbHashtreeResult::kFail;
+ }
+
+ if (status_ == AvbHandleStatus::kHashtreeDisabled ||
+ status_ == AvbHandleStatus::kVerificationDisabled) {
+ LINFO << "AVB HASHTREE disabled on: " << fstab_entry->mount_point;
+ return AvbHashtreeResult::kDisabled;
+ }
+
+ if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
+ fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+ return AvbHashtreeResult::kFail;
+ }
+
+ return AvbHashtreeResult::kSuccess;
+}
+
+bool AvbHandle::TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait) {
+ if (!fstab_entry) {
+ return false;
+ }
+
+ const std::string device_name(GetVerityDeviceName(*fstab_entry));
+
+ // TODO: remove duplicated code with UnmapDevice()
+ android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+ std::string path;
+ if (wait) {
+ dm.GetDmDevicePathByName(device_name, &path);
+ }
+ if (!dm.DeleteDevice(device_name)) {
+ return false;
+ }
+ if (!path.empty() && !WaitForFile(path, 1000ms, FileWaitMode::DoesNotExist)) {
+ return false;
+ }
+
+ return true;
+}
+
+std::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {
+ if (vbmeta_images_.size() < 1) {
+ return "";
+ }
+ std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+ fs_mgr_get_other_slot_suffix());
+ auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
+ return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
+}
+
+bool AvbHandle::IsDeviceUnlocked() {
+ return android::fs_mgr::IsDeviceUnlocked();
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
new file mode 100644
index 0000000..f82f83d
--- /dev/null
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb/fs_avb_util.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+#include <libdm/dm.h>
+
+#include "avb_util.h"
+#include "util.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ std::string* out_avb_partition_name,
+ VBMetaVerifyResult* out_verify_result) {
+ // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor
+ // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.
+ std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
+ fs_mgr_get_other_slot_suffix());
+ if (out_avb_partition_name) {
+ *out_avb_partition_name = avb_partition_name;
+ }
+
+ // Updates fstab_entry->blk_device from <partition> to /dev/block/dm-<N> if
+ // it's a logical partition.
+ std::string device_path = fstab_entry.blk_device;
+ if (fstab_entry.fs_mgr_flags.logical &&
+ !android::base::StartsWith(fstab_entry.blk_device, "/")) {
+ dm::DeviceMapper& dm = dm::DeviceMapper::Instance();
+ if (!dm.GetDmDevicePathByName(fstab_entry.blk_device, &device_path)) {
+ LERROR << "Failed to resolve logical device path for: " << fstab_entry.blk_device;
+ return nullptr;
+ }
+ }
+
+ return LoadAndVerifyVbmetaByPath(device_path, avb_partition_name, expected_public_key_blob,
+ true /* allow_verification_error */,
+ false /* rollback_protection */, false /* is_chained_vbmeta */,
+ out_public_key_data, nullptr /* out_verification_disabled */,
+ out_verify_result);
+}
+
+// Given a path, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& avb_partition_name, VBMetaData&& vbmeta) {
+ if (!vbmeta.size()) return nullptr;
+
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
new file mode 100644
index 0000000..521f2d5
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct VBMetaInfo {
+ std::string digest;
+ HashAlgorithm hash_algorithm;
+ size_t total_size;
+
+ VBMetaInfo() {}
+
+ VBMetaInfo(std::string digest_value, HashAlgorithm algorithm, size_t size)
+ : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}
+};
+
+class FsManagerAvbOps;
+
+class AvbHandle;
+using AvbUniquePtr = std::unique_ptr<AvbHandle>;
+
+// Provides a factory method to return a unique_ptr pointing to itself and the
+// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE
+// descriptors to load verity table into kernel through ioctl.
+class AvbHandle {
+ public:
+ // The factory methods to return a AvbUniquePtr that holds
+ // the verified AVB (external/avb) metadata of all verified partitions
+ // in vbmeta_images_.
+ //
+ // The metadata is checked against the following values from /proc/cmdline.
+ // - androidboot.vbmeta.{hash_alg, size, digest}.
+ //
+ // A typical usage will be:
+ // - AvbUniquePtr handle = AvbHandle::Open(); or
+ // - AvbUniquePtr handle = AvbHandle::LoadAndVerifyVbmeta();
+ //
+ // Possible return values:
+ // - nullptr: any error when reading and verifying the metadata,
+ // e.g., I/O error, digest value mismatch, size mismatch, etc.
+ //
+ // - a valid unique_ptr with status AvbHandleStatus::HashtreeDisabled:
+ // to support the existing 'adb disable-verity' feature in Android.
+ // It's very helpful for developers to make the filesystem writable to
+ // allow replacing binaries on the device.
+ //
+ // - a valid unique_ptr with status AvbHandleStatus::VerificationDisabled:
+ // to support 'avbctl disable-verification': only the top-level
+ // vbmeta is read, vbmeta structs in other partitions are not processed.
+ // It's needed to bypass AVB when using the generic system.img to run
+ // VTS for project Treble.
+ //
+ // - a valid unique_ptr with status AvbHandleStatus::VerificationError:
+ // there is verification error when libavb loads vbmeta from each
+ // partition. This is only allowed when the device is unlocked.
+ //
+ // - a valid unique_ptr with status AvbHandleStatus::Success: the metadata
+ // is verified and can be trusted.
+ //
+ // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
+ static AvbUniquePtr Open(); // loads inline vbmeta, via libavb.
+ static AvbUniquePtr LoadAndVerifyVbmeta(); // loads inline vbmeta.
+ static AvbUniquePtr LoadAndVerifyVbmeta(
+ const FstabEntry& fstab_entry); // loads offline vbmeta.
+ static AvbUniquePtr LoadAndVerifyVbmeta( // loads offline vbmeta.
+ const std::string& partition_name, const std::string& ab_suffix,
+ const std::string& ab_other_suffix, const std::string& expected_public_key,
+ const HashAlgorithm& hash_algorithm, bool allow_verification_error,
+ bool load_chained_vbmeta, bool rollback_protection,
+ std::function<std::string(const std::string&)> custom_device_path = nullptr);
+
+ // Sets up dm-verity on the given fstab entry.
+ // The 'wait_for_verity_dev' parameter makes this function wait for the
+ // verity device to get created before return.
+ //
+ // Return value:
+ // - kSuccess: successfully loads dm-verity table into kernel.
+ // - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,
+ // failed to get the HASHTREE descriptor, runtime error when set up
+ // device-mapper, etc.
+ // - kDisabled: hashtree is disabled.
+ AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);
+
+ // Similar to above, but loads the offline vbmeta from the end of fstab_entry->blk_device.
+ static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,
+ bool wait_for_verity_dev = true);
+
+ // Tear down dm devices created by SetUp[Standalone]AvbHashtree
+ // The 'wait' parameter makes this function wait for the verity device to get destroyed
+ // before return.
+ static bool TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait);
+
+ static bool IsDeviceUnlocked();
+
+ std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;
+
+ const std::string& avb_version() const { return avb_version_; }
+ const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }
+ AvbHandleStatus status() const { return status_; }
+
+ AvbHandle(const AvbHandle&) = delete; // no copy
+ AvbHandle& operator=(const AvbHandle&) = delete; // no assignment
+
+ AvbHandle(AvbHandle&&) noexcept = delete; // no move
+ AvbHandle& operator=(AvbHandle&&) noexcept = delete; // no move assignment
+
+ private:
+ AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
+
+ std::vector<VBMetaData> vbmeta_images_;
+ VBMetaInfo vbmeta_info_; // A summary info for vbmeta_images_.
+ AvbHandleStatus status_;
+ std::string avb_version_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
new file mode 100644
index 0000000..ec8badb
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <fs_avb/types.h>
+#include <fstab/fstab.h>
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Given a FstabEntry, loads and verifies the vbmeta.
+std::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+ const std::string& expected_public_key_blob,
+ std::string* out_public_key_data,
+ std::string* out_avb_partition_name,
+ VBMetaVerifyResult* out_verify_result);
+
+// Gets the hashtree descriptor for avb_partition_name from the vbmeta.
+std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
+ const std::string& avb_partition_name, VBMetaData&& vbmeta);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/types.h b/fs_mgr/libfs_avb/include/fs_avb/types.h
new file mode 100644
index 0000000..bd638e6
--- /dev/null
+++ b/fs_mgr/libfs_avb/include/fs_avb/types.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstring>
+#include <memory>
+#include <ostream>
+
+#include <libavb/libavb.h>
+
+namespace android {
+namespace fs_mgr {
+
+enum class VBMetaVerifyResult {
+ kSuccess = 0,
+ kError = 1,
+ kErrorVerification = 2,
+};
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);
+
+enum class AvbHashtreeResult {
+ kSuccess = 0,
+ kFail,
+ kDisabled,
+};
+
+enum class HashAlgorithm {
+ kInvalid = 0,
+ kSHA256 = 1,
+ kSHA512 = 2,
+};
+
+enum class AvbHandleStatus {
+ kSuccess = 0,
+ kUninitialized = 1,
+ kHashtreeDisabled = 2,
+ kVerificationDisabled = 3,
+ kVerificationError = 4,
+};
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status);
+
+struct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {
+ std::string partition_name;
+ std::string salt;
+ std::string root_digest;
+};
+
+class VBMetaData {
+ public:
+ // Constructors
+ VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};
+
+ VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {
+ // The ownership of data is NOT transferred, i.e., the caller still
+ // needs to release the memory as we make a copy here.
+ std::memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));
+ }
+
+ explicit VBMetaData(size_t size, const std::string& partition_name)
+ : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),
+ vbmeta_size_(size),
+ partition_name_(partition_name) {}
+
+ // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to
+ // true to update vbmeta_size_ to the actual size with valid content.
+ std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);
+
+ // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.
+ // e.g.,
+ // - /dev/block/by-name/system_a
+ // - /path/to/system_other.img.
+ void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }
+
+ // Get methods for each data member.
+ const std::string& partition() const { return partition_name_; }
+ const std::string& vbmeta_path() const { return vbmeta_path_; }
+ uint8_t* data() const { return vbmeta_ptr_.get(); }
+ const size_t& size() const { return vbmeta_size_; }
+
+ // Maximum size of a vbmeta data - 64 KiB.
+ static const size_t kMaxVBMetaSize = 64 * 1024;
+
+ private:
+ std::unique_ptr<uint8_t[]> vbmeta_ptr_;
+ size_t vbmeta_size_;
+ std::string partition_name_;
+ std::string vbmeta_path_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
new file mode 100755
index 0000000..5d2ce3d
--- /dev/null
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Run host tests
+atest libfs_avb_test # Tests public libfs_avb APIs.
+atest libfs_avb_internal_test # Tests libfs_avb private APIs.
+
+# Run device tests
+atest libfs_avb_device_test # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/fs_mgr_priv_sha.h b/fs_mgr/libfs_avb/sha.h
similarity index 92%
rename from fs_mgr/fs_mgr_priv_sha.h
rename to fs_mgr/libfs_avb/sha.h
index 5b53eea..2d3ca6d 100644
--- a/fs_mgr/fs_mgr_priv_sha.h
+++ b/fs_mgr/libfs_avb/sha.h
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-#ifndef __CORE_FS_MGR_PRIV_SHA_H
-#define __CORE_FS_MGR_PRIV_SHA_H
+#pragma once
#include <openssl/sha.h>
+namespace android {
+namespace fs_mgr {
+
class SHA256Hasher {
private:
SHA256_CTX sha256_ctx;
@@ -59,4 +61,5 @@
}
};
-#endif /* __CORE_FS_MGR_PRIV_SHA_H */
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
new file mode 100644
index 0000000..0d342d3
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -0,0 +1,1617 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+
+#include <android-base/unique_fd.h>
+#include <base/files/file_util.h>
+#include <base/rand_util.h>
+#include <base/strings/string_util.h>
+#include <libavb/libavb.h>
+
+#include "avb_util.h"
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbPartitionToDevicePatition;
+using android::fs_mgr::DeriveAvbPartitionName;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetAvbFooter;
+using android::fs_mgr::GetAvbPropertyDescriptor;
+using android::fs_mgr::GetChainPartitionInfo;
+using android::fs_mgr::GetTotalSize;
+using android::fs_mgr::LoadAndVerifyVbmetaByPartition;
+using android::fs_mgr::LoadAndVerifyVbmetaByPath;
+using android::fs_mgr::ValidatePublicKeyBlob;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+using android::fs_mgr::VerifyVBMetaData;
+using android::fs_mgr::VerifyVBMetaSignature;
+
+namespace fs_avb_host_test {
+
+class AvbUtilTest : public BaseFsAvbTest {
+ public:
+ AvbUtilTest(){};
+
+ protected:
+ ~AvbUtilTest(){};
+ // Helper function for VerifyVBMetaSignature test. Modifies vbmeta.data()
+ // in a number of places at |offset| of size |length| and checks that
+ // VerifyVBMetaSignature() returns |expected_result|.
+ bool TestVBMetaModification(VBMetaVerifyResult expected_result, const VBMetaData& vbmeta,
+ size_t offset, size_t length);
+ // Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+ void ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length);
+
+ // Loads the content of avb_image_path and comparies it with the content of vbmeta.
+ bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);
+
+ // Sets the flas in vbmeta header, the image_path could be a vbmeta.img or a system.img.
+ void SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags);
+};
+
+void AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags) {
+ if (!base::PathExists(image_path)) return;
+
+ std::string image_file_name = image_path.RemoveExtension().BaseName().value();
+ bool is_vbmeta_partition =
+ base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII);
+
+ android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+ EXPECT_TRUE(fd > 0);
+
+ uint64_t vbmeta_offset = 0; // for vbmeta.img
+ if (!is_vbmeta_partition) {
+ std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+ EXPECT_NE(nullptr, footer);
+ vbmeta_offset = footer->vbmeta_offset;
+ }
+
+ auto flags_offset = vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags);
+ uint32_t flags_data = htobe32(flags);
+ EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+ EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {
+ EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", ""));
+ EXPECT_EQ("system", AvbPartitionToDevicePatition("system", "", "_b"));
+
+ EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", ""));
+ EXPECT_EQ("system_a", AvbPartitionToDevicePatition("system", "_a", "_b"));
+
+ EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "", "_b"));
+ EXPECT_EQ("system_b", AvbPartitionToDevicePatition("system_other", "_a", "_b"));
+}
+
+TEST_F(AvbUtilTest, DeriveAvbPartitionName) {
+ // The fstab_entry to test.
+ FstabEntry fstab_entry = {
+ .blk_device = "/dev/block/dm-1", // a dm-linear device (logical)
+ .mount_point = "/system",
+ .fs_type = "ext4",
+ .logical_partition_name = "system",
+ };
+
+ // Logical partitions.
+ // non-A/B
+ fstab_entry.fs_mgr_flags.logical = true;
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+ // Active slot.
+ fstab_entry.fs_mgr_flags.slot_select = true;
+ fstab_entry.logical_partition_name = "system_a";
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+ EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+ // The other slot.
+ fstab_entry.fs_mgr_flags.slot_select = false;
+ fstab_entry.fs_mgr_flags.slot_select_other = true;
+ fstab_entry.logical_partition_name = "system_b";
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_b"));
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+ EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_wont_erase_b"));
+
+ // Non-logical partitions.
+ // non-A/B.
+ fstab_entry.fs_mgr_flags.logical = false;
+ fstab_entry.fs_mgr_flags.slot_select = false;
+ fstab_entry.fs_mgr_flags.slot_select_other = false;
+ fstab_entry.blk_device = "/dev/block/by-name/system";
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_dont_care", "_dont_care"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "", ""));
+ // Active slot _a.
+ fstab_entry.fs_mgr_flags.slot_select = true;
+ fstab_entry.blk_device = "/dev/block/by-name/system_a";
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_dont_care"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system", DeriveAvbPartitionName(fstab_entry, "_a", ""));
+ EXPECT_EQ("system_a", DeriveAvbPartitionName(fstab_entry, "_wont_erase_a", "_dont_care"));
+ // Inactive slot _b.
+ fstab_entry.fs_mgr_flags.slot_select = false;
+ fstab_entry.fs_mgr_flags.slot_select_other = true;
+ fstab_entry.blk_device = "/dev/block/by-name/system_b";
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_b"));
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "_a", "_b"));
+ EXPECT_EQ("system_other", DeriveAvbPartitionName(fstab_entry, "", "_b"));
+ EXPECT_EQ("system_b_other", DeriveAvbPartitionName(fstab_entry, "dont_care", "_wont_erase_b"));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSize) {
+ // Generates a raw test.img via BaseFsAvbTest.
+ const size_t image_size = 5 * 1024 * 1024;
+ base::FilePath image_path = GenerateImage("test.img", image_size);
+
+ // Checks file size is as expected via base::GetFileSize().
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(image_path, &file_size));
+ EXPECT_EQ(image_size, file_size);
+
+ // Checks file size is expected via libfs_avb internal utils.
+ auto fd = OpenUniqueReadFd(image_path);
+ EXPECT_EQ(image_size, GetTotalSize(fd));
+}
+
+TEST_F(AvbUtilTest, GetFdTotalSizeWithOffset) {
+ // Generates a raw test.img via BaseFsAvbTest.
+ const size_t image_size = 10 * 1024 * 1024;
+ base::FilePath image_path = GenerateImage("test.img", image_size);
+
+ // Checks file size is expected even with a non-zero offset at the beginning.
+ auto fd = OpenUniqueReadFd(image_path);
+ off_t initial_offset = 2019;
+ EXPECT_EQ(initial_offset, lseek(fd, initial_offset, SEEK_SET));
+ EXPECT_EQ(image_size, GetTotalSize(fd)); // checks that total size is still returned.
+ EXPECT_EQ(initial_offset, lseek(fd, 0, SEEK_CUR)); // checks original offset is restored.
+}
+
+TEST_F(AvbUtilTest, GetAvbFooter) {
+ // Generates a raw system.img
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+ EXPECT_NE(0U, system_path.value().size());
+
+ // Checks image size is as expected.
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+ EXPECT_EQ(image_size, file_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Checks partition size is as expected, after adding footer.
+ ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+ EXPECT_EQ(partition_size, file_size);
+
+ // Checks avb footer and avb vbmeta.
+ EXPECT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 15728640 bytes\n"
+ "Original image size: 10485760 bytes\n"
+ "VBMeta offset: 10661888\n"
+ "VBMeta size: 3648 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 1088 bytes\n"
+ "Auxiliary Block: 2304 bytes\n"
+ "Algorithm: SHA512_RSA8192\n"
+ "Rollback Index: 20\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 10485760 bytes\n"
+ " Tree Offset: 10485760\n"
+ " Tree Size: 86016 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 10571776\n"
+ " FEC size: 90112 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+ " Flags: 0\n",
+ InfoImage(system_path));
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_NE(nullptr, footer);
+ EXPECT_EQ(10485760, footer->original_image_size);
+ EXPECT_EQ(10661888, footer->vbmeta_offset);
+ EXPECT_EQ(3648, footer->vbmeta_size);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterErrorVerification) {
+ // Generates a raw system.img
+ const size_t image_size = 5 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) {
+ // Generates a raw system.img
+ const size_t image_size = AVB_FOOTER_SIZE - 10;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Checks each field from GetAvbFooter(fd).
+ auto fd = OpenUniqueReadFd(system_path);
+ auto footer = GetAvbFooter(fd);
+ EXPECT_EQ(nullptr, footer);
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {
+ // Makes a vbmeta.img with some properties.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {}, /* chain_partitions */
+ "--prop foo:android "
+ "--prop bar:treble "
+ "--internal_release_string \"unit test\" ");
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+ // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+
+ EXPECT_EQ("android", GetAvbPropertyDescriptor("foo", vbmeta_images));
+ EXPECT_EQ("treble", GetAvbPropertyDescriptor("bar", vbmeta_images));
+ EXPECT_EQ("", GetAvbPropertyDescriptor("non-existent", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--prop com.android.build.system.security_patch:2019-04-05 "
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta.img including the 'system' chained descriptor.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {boot_path}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+ // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.
+ std::vector<VBMetaData> vbmeta_images;
+ vbmeta_images.emplace_back(std::move(vbmeta));
+ vbmeta_images.emplace_back(std::move(system_vbmeta));
+
+ EXPECT_EQ("2019-04-05",
+ GetAvbPropertyDescriptor("com.android.build.system.security_patch", vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, GetVBMetaHeader) {
+ // Generates a raw boot.img
+ const size_t image_size = 5 * 1024 * 1024;
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", image_size);
+ // Appends AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+ base::FilePath boot_vbmeta = ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 1216 bytes\n"
+ "Algorithm: SHA256_RSA4096\n"
+ "Rollback Index: 10\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+ " Flags: 0\n",
+ InfoImage("boot-vbmeta.img"));
+
+ // Creates a VBMetaData with the content from boot-vbmeta.img.
+ std::string content;
+ EXPECT_TRUE(base::ReadFileToString(boot_vbmeta, &content));
+ VBMetaData vbmeta((uint8_t*)content.data(), content.size(), "boot-vbmeta");
+ EXPECT_EQ(content.size(), vbmeta.size());
+
+ // Checks each field returned from GetVBMetaHeader().
+ auto vbmeta_header = vbmeta.GetVBMetaHeader(false /* update_vbmeta_size */);
+ EXPECT_NE(nullptr, vbmeta_header);
+ EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+ EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+ EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+ EXPECT_EQ(0, vbmeta_header->hash_offset);
+ EXPECT_EQ(32, vbmeta_header->hash_size);
+ EXPECT_EQ(32, vbmeta_header->signature_offset);
+ EXPECT_EQ(512, vbmeta_header->signature_size);
+ EXPECT_EQ(176, vbmeta_header->public_key_offset);
+ EXPECT_EQ(1032, vbmeta_header->public_key_size);
+ EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+ EXPECT_EQ(176, vbmeta_header->descriptors_size);
+ EXPECT_EQ(10, vbmeta_header->rollback_index);
+ EXPECT_EQ(0, vbmeta_header->flags);
+ EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+ // Appends some garbage to the end of the vbmeta buffer, checks it still can work.
+ std::string padding(2020, 'A'); // Generate a padding with length 2020.
+ std::string content_padding = content + padding;
+ VBMetaData vbmeta_padding((const uint8_t*)content_padding.data(), content_padding.size(),
+ "boot");
+ EXPECT_EQ(content_padding.size(), vbmeta_padding.size());
+
+ // Checks each field still can be parsed properly, even with garbage padding.
+ vbmeta_header = vbmeta_padding.GetVBMetaHeader(false /* update_vbmeta_size */);
+ EXPECT_NE(nullptr, vbmeta_header);
+ EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);
+ EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);
+ EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);
+ EXPECT_EQ(0, vbmeta_header->hash_offset);
+ EXPECT_EQ(32, vbmeta_header->hash_size);
+ EXPECT_EQ(32, vbmeta_header->signature_offset);
+ EXPECT_EQ(512, vbmeta_header->signature_size);
+ EXPECT_EQ(176, vbmeta_header->public_key_offset);
+ EXPECT_EQ(1032, vbmeta_header->public_key_size);
+ EXPECT_EQ(0, vbmeta_header->descriptors_offset);
+ EXPECT_EQ(176, vbmeta_header->descriptors_size);
+ EXPECT_EQ(10, vbmeta_header->rollback_index);
+ EXPECT_EQ(0, vbmeta_header->flags);
+ EXPECT_EQ("unit test", std::string((const char*)vbmeta_header->release_string));
+
+ // Checks vbmeta size is updated to the actual size without padding.
+ vbmeta_header = vbmeta_padding.GetVBMetaHeader(true /* update_vbmeta_size */);
+ EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob) {
+ // Generates a raw key.bin
+ const size_t key_size = 2048;
+ base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+ uint8_t key_data[key_size];
+ EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+ std::string expected_key_blob;
+ EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+
+ key_data[10] ^= 0x80; // toggles a bit and expects a failure
+ EXPECT_FALSE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+ key_data[10] ^= 0x80; // toggles the bit again, should pass
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {
+ // Generates a raw key.bin
+ const size_t key_size = 2048;
+ base::FilePath key_path = GenerateImage("key.bin", key_size);
+
+ uint8_t key_data[key_size];
+ EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));
+
+ std::string expected_key_blob = ""; // empty means no expectation, thus return true.
+ EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));
+}
+
+TEST_F(AvbUtilTest, ValidatePublicKeyBlob_MultipleAllowedKeys) {
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ std::vector<std::string> allowed_key_paths;
+ allowed_key_paths.push_back(rsa2048_public_key.value());
+ allowed_key_paths.push_back(rsa4096_public_key.value());
+
+ std::string expected_key_blob_2048;
+ EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+ std::string expected_key_blob_8192;
+ EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_2048, allowed_key_paths));
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_4096, allowed_key_paths));
+
+ EXPECT_FALSE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+ EXPECT_FALSE(ValidatePublicKeyBlob("invalid_content", allowed_key_paths));
+ EXPECT_FALSE(ValidatePublicKeyBlob("", allowed_key_paths));
+
+ allowed_key_paths.push_back(rsa8192_public_key.value());
+ EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignature) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+ auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+ "hashtree", signing_key, "SHA256_RSA4096",
+ 10 /* rollback_index */);
+
+ auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ VerifyVBMetaSignature(vbmeta, expected_public_key_blob,
+ nullptr /* out_public_key_data */));
+
+ // Converts the expected key into an 'unexpected' key.
+ expected_public_key_blob[10] ^= 0x80;
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ VerifyVBMetaSignature(vbmeta, expected_public_key_blob,
+ nullptr /* out_public_key_data */));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureOutputPublicKeyData) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+ auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+ "hashtree", signing_key, "SHA256_RSA4096",
+ 10 /* rollback_index */);
+ std::string out_public_key_data;
+ auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));
+ EXPECT_EQ(out_public_key_data, expected_public_key_blob);
+
+ // Converts the expected key into an 'unexpected' key.
+ expected_public_key_blob[10] ^= 0x80;
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));
+ EXPECT_NE(out_public_key_data, expected_public_key_blob);
+}
+
+bool AvbUtilTest::TestVBMetaModification(VBMetaVerifyResult expected_result,
+ const VBMetaData& vbmeta, size_t offset, size_t length) {
+ uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta.data());
+ const int kNumCheckIntervals = 8;
+
+ // Tests |kNumCheckIntervals| modifications in the start, middle, and
+ // end of the given sub-array at offset with size.
+ for (int n = 0; n <= kNumCheckIntervals; n++) {
+ size_t o = std::min(length * n / kNumCheckIntervals, length - 1) + offset;
+ d[o] ^= 0x80;
+ VBMetaVerifyResult result = VerifyVBMetaSignature(vbmeta, "" /* expected_public_key_blob */,
+ nullptr /* out_public_key_data */);
+ d[o] ^= 0x80;
+ if (result != expected_result) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureWithModification) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto signing_key = data_dir_.Append("testkey_rsa4096.pem");
+ auto vbmeta = GenerateImageAndExtractVBMetaData("system", image_size, partition_size,
+ "hashtree", signing_key, "SHA256_RSA4096",
+ 10 /* rollback_index */);
+
+ auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + header->authentication_data_block_size;
+
+ // Should detect modifications in the auxiliary data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ auxiliary_block_offset, header->auxiliary_data_block_size));
+
+ // Sholud detect modifications in the hash part of authentication data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ authentication_block_offset + header->hash_offset,
+ header->hash_size));
+
+ // Sholud detect modifications in the signature part of authentication data block.
+ EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,
+ authentication_block_offset + header->signature_offset,
+ header->signature_size));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureNotSigned) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ auto vbmeta = GenerateImageAndExtractVBMetaData(
+ "system", image_size, partition_size, "hashtree", {} /* avb_signing_key */,
+ "" /* avb_algorithm */, 10 /* rollback_index */);
+
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ VerifyVBMetaSignature(vbmeta, "" /* expected_public_key_blob */,
+ nullptr /* out_public_key_data */));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaSignatureInvalidVBMeta) {
+ const size_t buffer_size = 5 * 1024 * 1024;
+ std::vector<uint8_t> vbmeta_buffer(buffer_size);
+ for (size_t n = 0; n < buffer_size; n++) {
+ vbmeta_buffer[n] = uint8_t(n);
+ }
+
+ VBMetaData invalid_vbmeta((const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(),
+ "invalid_vbmeta");
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ VerifyVBMetaSignature(invalid_vbmeta, "" /* expected_public_key_blob */,
+ nullptr /* out_public_Key_data */));
+}
+
+bool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,
+ const VBMetaData& expected_vbmeta) {
+ if (!base::PathExists(avb_image_path)) return false;
+
+ std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();
+
+ base::FilePath extracted_vbmeta_path;
+ if (base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII)) {
+ extracted_vbmeta_path = avb_image_path; // no need to extract if it's a vbmeta image.
+ } else {
+ extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + "-vbmeta.img");
+ }
+
+ // Gets file size of the vbmeta image.
+ int64_t extracted_vbmeta_size;
+ EXPECT_TRUE(base::GetFileSize(extracted_vbmeta_path, &extracted_vbmeta_size));
+
+ // Reads the vbmeta into a vector.
+ std::vector<uint8_t> extracted_vbmeta_content(extracted_vbmeta_size);
+ EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path,
+ reinterpret_cast<char*>(extracted_vbmeta_content.data()),
+ extracted_vbmeta_size));
+
+ // Compares extracted_vbmeta_content with the expected_vbmeta.
+ EXPECT_EQ(expected_vbmeta.size(), extracted_vbmeta_size);
+ return memcmp(reinterpret_cast<void*>(extracted_vbmeta_content.data()),
+ reinterpret_cast<void*>(expected_vbmeta.data()), extracted_vbmeta_size) == 0;
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 1088 bytes\n"
+ "Auxiliary Block: 3840 bytes\n"
+ "Algorithm: SHA256_RSA8192\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: boot\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: system\n"
+ " Rollback Index Location: 2\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta.img"));
+
+ android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+ fd, "vbmeta", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+ EXPECT_TRUE(vbmeta != nullptr);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+ EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+ // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+ vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataWithFooter) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+ fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+ EXPECT_TRUE(vbmeta != nullptr);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+ EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+ // Checkes the returned vbmeta content is the same as that extracted via avbtool.
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+// Modifies a random bit for a file, in the range of [offset, offset + length - 1].
+// Length < 0 means only resets previous modification without introducing new modification.
+void AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length) {
+ static int last_modified_location = -1;
+ static std::string last_file_path;
+
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(file_path, &file_size));
+
+ std::vector<uint8_t> file_content(file_size);
+ ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast<char*>(file_content.data()), file_size));
+
+ // Resets previous modification for consecutive calls on the same file.
+ if (last_file_path == file_path.value()) {
+ file_content[last_modified_location] ^= 0x80;
+ }
+
+ // Introduces a new modification.
+ if (length > 0) {
+ int modify_location = base::RandInt(offset, offset + length - 1);
+ file_content[modify_location] ^= 0x80;
+ last_file_path = file_path.value();
+ last_modified_location = modify_location;
+ }
+
+ ASSERT_EQ(file_size, static_cast<const size_t>(base::WriteFile(
+ file_path, reinterpret_cast<const char*>(file_content.data()),
+ file_content.size())));
+}
+
+TEST_F(AvbUtilTest, VerifyVBMetaDataError) {
+ const size_t image_size = 10 * 1024 * 1024;
+ const size_t partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0);
+
+ std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);
+ EXPECT_TRUE(footer != nullptr);
+
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
+ fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+
+ auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa8192.pem"));
+ EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);
+
+ // Modifies hash and signature, checks there is verification error.
+ auto header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+ // Modifies the hash.
+ ModifyFile(system_path,
+ footer->vbmeta_offset + authentication_block_offset + header->hash_offset,
+ header->hash_size);
+ android::base::unique_fd hash_modified_fd(
+ open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(hash_modified_fd > 0);
+ // Should return ErrorVerification.
+ vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
+ nullptr /* out_public_key_data */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+ // Modifies the auxiliary data block.
+ size_t auxiliary_block_offset =
+ authentication_block_offset + header->authentication_data_block_size;
+ ModifyFile(system_path, footer->vbmeta_offset + auxiliary_block_offset,
+ header->auxiliary_data_block_size);
+ android::base::unique_fd aux_modified_fd(
+ open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(aux_modified_fd > 0);
+ // Should return ErrorVerification.
+ vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
+ nullptr /* out_public_key_data */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+ // Resets previous modification by setting offset to -1, and checks the verification can pass.
+ ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+ android::base::unique_fd ok_fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(ok_fd > 0);
+ // Should return ResultOK..
+ vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */,
+ nullptr /* out_public_key_data */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfo) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system.img including the 'system' chained descriptor.
+ GenerateVBMetaImage("vbmeta_system.img", "SHA256_RSA4096", 0,
+ data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ // Loads the key blobs for comparison.
+ std::string expected_key_blob_2048;
+ EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+ // Checks chain descriptors in vbmeta.img
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 1088 bytes\n"
+ "Auxiliary Block: 3840 bytes\n"
+ "Algorithm: SHA256_RSA8192\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: boot\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: vbmeta_system\n"
+ " Rollback Index Location: 2\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta.img"));
+
+ bool fatal_error = false;
+ auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+ EXPECT_EQ(2, chained_descriptors.size()); // contains 'boot' and 'vbmeta_system'.
+ EXPECT_EQ(false, fatal_error);
+
+ EXPECT_EQ("boot", chained_descriptors[0].partition_name);
+ EXPECT_EQ(expected_key_blob_2048, chained_descriptors[0].public_key_blob);
+
+ EXPECT_EQ("vbmeta_system", chained_descriptors[1].partition_name);
+ EXPECT_EQ(expected_key_blob_4096, chained_descriptors[1].public_key_blob);
+
+ // Checks chain descriptors in vbmeta_system.img
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 2176 bytes\n"
+ "Algorithm: SHA256_RSA4096\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: system\n"
+ " Rollback Index Location: 3\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta_system.img"));
+
+ chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
+ EXPECT_EQ(1, chained_descriptors.size()); // contains 'system' only.
+ EXPECT_EQ(false, fatal_error);
+ EXPECT_EQ("system", chained_descriptors[0].partition_name);
+ EXPECT_EQ(expected_key_blob_4096, chained_descriptors[0].public_key_blob);
+}
+
+TEST_F(AvbUtilTest, GetChainPartitionInfoNone) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+ {boot_path, system_path}, /* include_descriptor_image_paths */
+ {}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+ EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 960 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+ " Flags: 0\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 10485760 bytes\n"
+ " Tree Offset: 10485760\n"
+ " Tree Size: 86016 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 10571776\n"
+ " FEC size: 90112 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+ " Flags: 0\n",
+ InfoImage("vbmeta.img"));
+
+ // Checks none of chain descriptors is found.
+ bool fatal_error = false;
+ auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta.img"), &fatal_error);
+ EXPECT_EQ(0, chained_descriptors.size()); // There is no chain descriptors.
+ EXPECT_EQ(false, fatal_error);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPath) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+ 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ std::string expected_key_blob_4096 =
+ ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+ bool verification_disabled;
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, &out_public_key_data, &verification_disabled,
+ &verify_result);
+
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
+ EXPECT_EQ(false, verification_disabled);
+ EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+
+ EXPECT_EQ(2112UL, vbmeta->size());
+ EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+ EXPECT_EQ("system_other", vbmeta->partition());
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathErrorVerification) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+ 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ std::string expected_key_blob_4096 =
+ ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Modifies the auxiliary data of system_other.img
+ auto fd = OpenUniqueReadFd(system_path);
+ auto system_footer = GetAvbFooter(fd);
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system_other-vbmeta.img");
+ auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + system_header->authentication_data_block_size;
+
+ // Modifies the hash.
+ ModifyFile(
+ system_path,
+ (system_footer->vbmeta_offset + authentication_block_offset + system_header->hash_offset),
+ system_header->hash_size);
+
+ VBMetaVerifyResult verify_result;
+ // Not allow verification error.
+ std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_EQ(nullptr, vbmeta);
+
+ // Allow verification error.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ true /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+
+ EXPECT_EQ(2112UL, vbmeta->size());
+ EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+ EXPECT_EQ("system_other", vbmeta->partition());
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+ // Modifies the auxiliary data block.
+ ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+ system_header->auxiliary_data_block_size);
+
+ // Not allow verification error.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_EQ(nullptr, vbmeta);
+
+ // Allow verification error.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ true /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathUnexpectedPublicKey) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+ 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ std::string unexpected_key_blob_2048 =
+ ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa2048.pem"));
+ std::string expected_key_blob_4096 =
+ ExtractPublicKeyAvbBlob(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Uses the correct expected public key.
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, &out_public_key_data,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(verify_result, VBMetaVerifyResult::kSuccess);
+ EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+ EXPECT_EQ(2112UL, vbmeta->size());
+ EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+ EXPECT_EQ("system_other", vbmeta->partition());
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+ // Uses the wrong expected public key with allow_verification_error set to false.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", unexpected_key_blob_2048,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, &out_public_key_data,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_EQ(nullptr, vbmeta);
+ // Checks out_public_key_data is still loaded properly, if the error is due
+ // to an unexpected public key instead of vbmeta image verification error.
+ EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+
+ // Uses the wrong expected public key with allow_verification_error set to true.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", unexpected_key_blob_2048,
+ true /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, &out_public_key_data,
+ nullptr /* verification_disabled */, &verify_result);
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(expected_key_blob_4096, out_public_key_data);
+ EXPECT_EQ(verify_result, VBMetaVerifyResult::kErrorVerification);
+ EXPECT_EQ(2112UL, vbmeta->size());
+ EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+ EXPECT_EQ("system_other", vbmeta->partition());
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathVerificationDisabled) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system_other.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system_other", system_partition_size, "SHA512_RSA4096",
+ 20, data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+
+ // Sets disabled flag and expect the returned verification_disabled is true.
+ SetVBMetaFlags(system_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ bool verification_disabled;
+ VBMetaVerifyResult verify_result;
+ std::string out_public_key_data;
+ std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ true /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ &verification_disabled, &verify_result);
+
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
+ EXPECT_EQ(true, verification_disabled); // should be true.
+
+ EXPECT_EQ(2112UL, vbmeta->size());
+ EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());
+ EXPECT_EQ("system_other", vbmeta->partition());
+ EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
+
+ // Since the vbmeta flags is modified, vbmeta will be nullptr
+ // if verification error isn't allowed.
+ vbmeta = LoadAndVerifyVbmetaByPath(
+ system_path.value(), "system_other", expected_key_blob_4096,
+ false /* allow_verification_error */, false /* rollback_protection */,
+ false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,
+ &verification_disabled, &verify_result);
+ EXPECT_EQ(nullptr, vbmeta);
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartition) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system.img including the 'system' chained descriptor.
+ auto vbmeta_system_path = GenerateVBMetaImage(
+ "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Starts to test LoadAndVerifyVbmetaByPartition.
+ std::vector<VBMetaData> vbmeta_images;
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot, vbmeta_system and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+ // Skip loading chained vbmeta images.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Only vbmeta is loaded.
+ EXPECT_EQ(1UL, vbmeta_images.size());
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionWithSuffixes) {
+ // Tests the following chained partitions.
+ // vbmeta_a.img
+ // |--> boot_b.img (boot_other)
+ // |--> vbmeta_system_b.img (vbmeta_system_other)
+ // |--> system_a.img
+
+ // Generates a raw boot_b.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot_b.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system_a.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system_a.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system_b.img including the 'system' chained descriptor.
+ auto vbmeta_system_path = GenerateVBMetaImage(
+ "vbmeta_system_b.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta_a.img includeing 'boot_other' and 'vbmeta_system_other' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage(
+ "vbmeta_a.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot_other", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system_other", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Starts to test LoadAndVerifyVbmetaByPartition with ab_suffix and ab_other_suffix.
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+
+ std::vector<VBMetaData> vbmeta_images;
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot_other, vbmeta_system_other and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+ // Skips loading chained vbmeta images.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */, "_b" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Only vbmeta is loaded.
+ EXPECT_EQ(1UL, vbmeta_images.size());
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+ // Using an invalid suffix for 'other' slot, checks it returns error.
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "_a" /* ab_suffix */,
+ "_invalid_suffix" /* other_suffix */, "" /* expected_public_key_blob*/,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path, false /* is_chained_vbmeta*/,
+ &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionErrorVerification) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ auto vbmeta = LoadVBMetaData("vbmeta.img");
+
+ // Modifies hash, checks there is error if allow_verification_error is false.
+ auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t header_block_offset = 0;
+ size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);
+
+ // Modifies the hash.
+ ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);
+
+ // Starts to test LoadAndVerifyVbmetaByPartition.
+ std::vector<VBMetaData> vbmeta_images;
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+ // Stops to load vbmeta because the top-level vbmeta has verification error.
+ EXPECT_EQ(0UL, vbmeta_images.size());
+
+ // Tries again with verification error allowed.
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "", /* other_suffix */
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(3UL, vbmeta_images.size()); // vbmeta, boot, and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[2]));
+
+ // Resets the modification of the hash.
+ ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);
+
+ // Modifies the auxiliary data of system.img
+ auto fd = OpenUniqueReadFd(system_path);
+ auto system_footer = GetAvbFooter(fd);
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+ auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);
+ size_t auxiliary_block_offset =
+ authentication_block_offset + system_header->authentication_data_block_size;
+
+ // Modifies the auxiliary data block.
+ ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,
+ system_header->auxiliary_data_block_size);
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+ // 'vbmeta', 'boot' but no 'system', because of verification error.
+ EXPECT_EQ(2UL, vbmeta_images.size());
+ // Binary comparison for the loaded 'vbmeta' and 'boot'.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+
+ // Resets the modification of the auxiliary data.
+ ModifyFile(system_path, 0 /* offset */, -1 /* length */);
+
+ // Sets the vbmeta header flags on a chained partition, which introduces an error.
+ ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),
+ sizeof(uint32_t));
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionVerificationDisabled) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ // Makes a vbmeta_system.img including the 'system' chained descriptor.
+ auto vbmeta_system_path = GenerateVBMetaImage(
+ "vbmeta_system.img", "SHA256_RSA4096", 0, data_dir_.Append("testkey_rsa4096.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"system", 3, rsa4096_public_key}}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"vbmeta_system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Starts to test LoadAndVerifyVbmetaByPartition.
+ std::vector<VBMetaData> vbmeta_images;
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, false /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot, vbmeta_system and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+
+ // Sets VERIFICATION_DISABLED to the top-level vbmeta.img
+ SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+ EXPECT_EQ(1UL, vbmeta_images.size()); // Only vbmeta is loaded
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+
+ // HASHTREE_DISABLED still loads the chained vbmeta.
+ SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,
+ false /* is_chained_vbmeta*/, &vbmeta_images));
+ EXPECT_EQ(4UL, vbmeta_images.size()); // vbmeta, boot, vbmeta_system and system
+ // Binary comparison for each vbmeta image.
+ EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));
+ EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));
+ EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));
+ EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));
+}
+
+TEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionUnexpectedPublicKey) {
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+ std::string expected_key_blob_4096;
+ EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));
+ std::string expected_key_blob_8192;
+ EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ std::vector<VBMetaData> vbmeta_images;
+ // Uses the correct expected public key.
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_8192, true /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ // Uses the wrong expected public key with allow_verification_error set to true.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_4096, true /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+
+ // Uses the wrong expected public key with allow_verification_error set to false.
+ vbmeta_images.clear();
+ EXPECT_EQ(VBMetaVerifyResult::kError,
+ LoadAndVerifyVbmetaByPartition(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ expected_key_blob_4096, false /* allow_verification_error */,
+ false /* load_chained_vbmeta */, true /* rollback_protection */,
+ vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
new file mode 100644
index 0000000..5a1cd0d
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+TEST_F(BaseFsAvbTest, GenerateImage) {
+ const size_t image_size = 5 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", image_size);
+ EXPECT_NE(0U, boot_path.value().size());
+
+ // Checks file size is as expected.
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+ EXPECT_EQ(file_size, image_size);
+
+ // Checks file content is as expected.
+ std::vector<uint8_t> expected_content;
+ expected_content.resize(image_size);
+ for (size_t n = 0; n < image_size; n++) {
+ expected_content[n] = uint8_t(n);
+ }
+ std::vector<uint8_t> actual_content;
+ actual_content.resize(image_size);
+ EXPECT_TRUE(
+ base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));
+ EXPECT_EQ(expected_content, actual_content);
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImage) {
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+ {}, /* include_descriptor_image_paths */
+ {}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+ EXPECT_EQ("5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 576 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " (none)\n",
+ InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashFooter) {
+ // Generates a raw boot.img
+ const size_t image_size = 5 * 1024 * 1024;
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", image_size);
+ EXPECT_NE(0U, boot_path.value().size());
+ // Checks file size is as expected.
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));
+ EXPECT_EQ(file_size, image_size);
+ // Appends AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+ ExtractVBMetaImage(boot_path, "boot-vbmeta.img");
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 1216 bytes\n"
+ "Algorithm: SHA256_RSA4096\n"
+ "Rollback Index: 10\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+ " Flags: 0\n",
+ InfoImage("boot-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, AddHashtreeFooter) {
+ // Generates a raw system.img
+ const size_t image_size = 50 * 1024 * 1024;
+ const size_t partition_size = 60 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", image_size);
+ EXPECT_NE(0U, system_path.value().size());
+ // Checks file size is as expected.
+ int64_t file_size;
+ ASSERT_TRUE(base::GetFileSize(system_path, &file_size));
+ EXPECT_EQ(file_size, image_size);
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts system vbmeta from system.img into system-vbmeta.img.
+ ExtractVBMetaImage(system_path, "system-vbmeta.img");
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 1088 bytes\n"
+ "Auxiliary Block: 2304 bytes\n"
+ "Algorithm: SHA512_RSA8192\n"
+ "Rollback Index: 20\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 52428800 bytes\n"
+ " Tree Offset: 52428800\n"
+ " Tree Size: 413696 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 52842496\n"
+ " FEC size: 417792 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: d20d40c02298e385ab6d398a61a3b91dc9947d99\n"
+ " Flags: 0\n",
+ InfoImage("system-vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA8192", 20,
+ data_dir_.Append("testkey_rsa8192.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Makes a vbmeta.img including both 'boot' and 'system' descriptors.
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA2048", 0, data_dir_.Append("testkey_rsa2048.pem"),
+ {boot_path, system_path}, /* include_descriptor_image_paths */
+ {}, /* chain_partitions */
+ "--internal_release_string \"unit test\"");
+ EXPECT_EQ("a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 320 bytes\n"
+ "Auxiliary Block: 960 bytes\n"
+ "Algorithm: SHA256_RSA2048\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hash descriptor:\n"
+ " Image Size: 5242880 bytes\n"
+ " Hash Algorithm: sha256\n"
+ " Partition Name: boot\n"
+ " Salt: d00df00d\n"
+ " Digest: "
+ "222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\n"
+ " Flags: 0\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 10485760 bytes\n"
+ " Tree Offset: 10485760\n"
+ " Tree Size: 86016 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 10571776\n"
+ " FEC size: 90112 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+ " Flags: 0\n",
+ InfoImage("vbmeta.img"));
+}
+
+TEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Make a vbmeta image with chain partitions.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0, data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ(
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 1088 bytes\n"
+ "Auxiliary Block: 3840 bytes\n"
+ "Algorithm: SHA256_RSA8192\n"
+ "Rollback Index: 0\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: boot\n"
+ " Rollback Index Location: 1\n"
+ " Public key (sha1): cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+ " Chain Partition descriptor:\n"
+ " Partition Name: system\n"
+ " Rollback Index Location: 2\n"
+ " Public key (sha1): 2597c218aae470a130f61162feaae70afd97f011\n",
+ InfoImage("vbmeta.img"));
+}
+
+} // namespace fs_avb_host_test
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
new file mode 100644
index 0000000..867dcff
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh
+4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ
+gXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt
+DfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM
+uXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct
+YbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn
+SXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd
+jJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp
+z9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN
+mQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT
+o/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG
+zGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9
+5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp
+BRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX
+vyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu
+i+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2
+iQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW
+mIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY
+b7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy
+oWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A
+lBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF
+nRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT
+PudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A
+vWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow
+GH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
new file mode 100644
index 0000000..26db5c3
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA
+uwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83
+NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb
+IwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64
+ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf
+upuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ
+X39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY
+RzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev
+SCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe
+ke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g
+Rcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA
+AQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy
+n7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q
+toGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO
+b5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y
+Tv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k
+tLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK
++tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF
+cth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY
+dIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP
+yKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh
+2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj
+8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG
+bQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4
+aEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4
+sDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom
+O+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF
+UVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd
+c/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U
+Z9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F
+Rr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq
+YflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi
+bwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ
+hdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU
+HkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4
+GhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL
+RrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60
+fHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla
+0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN
+PUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu
+PZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33
+IZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV
+ktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL
+P5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D
+ufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr
+4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s
+vM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw
+E6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML
+Xgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
new file mode 100644
index 0000000..a383428
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIISKgIBAAKCBAEA0D3T+dISsmCHm797wsX0vVfqUWDJ/3mvDYozlCabDhnGLlSE
+pAQbf1Z8Ts+OM4pVRHOJUJL0WebNdmPPGjsyWQz6zZE96lQZL3avCEXqYVQR66V5
+3wdK/ohaMSRnGyEMBrqkVVbF3gCr+/irxD3YK+VowO2WKs/6GrMdqTA8Y5CTF/Je
+ptwsSg5MMjr6UaK4qDcrej3hkgBVGvRV3cj1snK6Br8HuYdFnpGGTS0d7UJlHFgl
+trGHU/CBO923hkHgJaWEjC0giSGjhKKtLzrVcpDV2y/lWQP9T/T4djEAIaHqQ++P
+SdOSR6psIGR6hVgSigt7HCnE7nW711/rfV5Ur9EiVpB040mDImKZcy8//TMnXydN
+1KYTVd/34fdpzMpSw5iblErbwOLXVTUmOztYnpl41feHSv/jPesHstPlfklIF2vo
+GZEohf9scQvcuM7wEBfC/aTA9K39zMmkBbcvSZjLyhmcSZWMPPOZyIcl3zY53QhW
+QC/abmIcBfI1S4+r7mC4i2Jn++oEvuGNVGr2SY2Z0ZZxXGL1HI/08D/3+Tcumrcn
+4YjPK/DMFi0F+e+1x41lipuf+cx/2qRNQX/m02STrLYdM6e0g33KvlnFdi2b752y
+/OIaMwxDaJvunMh6EMDWKM1AHbY/ioAoK7eS26HeJLEDllqO4+SWP37c8lMvSEWy
+1GiErR0HcsOj/QwWGPFseoVroMiA2sUQ0Ic/tgVjCTlXg+12XpUnouIweCi8KcL/
+ad2zJkju9hBhJLBQ/2GnivJi3lFgF4Gd//TSJ6rgWuXFfMKt/9z2Sz35ohEX4yA0
+flqlCeLInFEoevbz+XT9aRfDe65MZ79yw3TfP9CrV74hf1RRzveD4zpi3F+hcY2i
+JWsH7gROZeCm6fAX5Trecd3hOxJOfA4N4rvSSCq6BwCvebT8FY25Z/VF7cQrHYDS
+ij5w6lqhMzXHeUEY90Ga9AK4XzaWwGgezq+R7Zs00YSKqFv9qYNKdR7tz3cjijWf
+9q/3R1uh6EQKTMZKo4SEClJiGyjOBvmPK09jMFZTJv00hDxagDPZBl7XpLDJ5/Ln
+1uppvLCNWWY1zeJfaElMyq3/PqKZLidF9rVoA1SIwk2lpdUvPote2oFiwCZoXlwZ
+J2ncjmXgQNs76/8unDJA0rj4JPqccw4M5GxQ7okbgm3F4rmzriCuv8BeMSCkr2ry
+0mY3UhpohX4wCMq0G4x5sEUAz9FVVPZKjxnYBmLDzrJAR+4+G7gZsct01XDJYgDd
+JVYInFP22/cIre8VrFWYtHbgOFdNqUiVq58de6PdZG/E+uaWmEThSlRrgEjTxupi
+OXfgdKW/20j1qAtjOlqFwsY094Q5rqULQ6wPxQIDAQABAoIEAQChmkmlhrRBv42d
+fYUiyxK52b8ath0saJdDz6tlXmxYDgJxM9/XlORt9oTzeDknoEO5olu+rrx4BBgQ
+tzYiaiwRVXRREVTWQ7tjzRvaNL/GFkLt93XTccpuKwyrNE/bitLVagRbwcI+HZFa
+MknCOihHMHoRto8h3FKAY94xzSAgODMek1WG8jhgpCXXmVNnBPt+d4oDDIDAGAfz
+qgf03J5nhIb+80KgZOzPOKnbvJaL6EmlLHbgB3c42dzAw7hHtVmofYGWcvLb2MIY
+DVKO435/sQx1U/8NDH6JjVdACZjLgObXH9K3/Tt46DWPEcrPLmD8xhoc6gFM+Qr0
+AhkzKoBYDNk0CljbhdIBXjktXU6wRQFZ45uP2e4JZ4zrzGBLr/t4lTavZ0SQtLld
+A6kOsGh+dCWFDtnshxYnl/xad/yR+3a5zmDJbo/fJTBXrlf1B4rfQkFtK20etOPQ
+B++FC/rjh3Mm/Kb/p9Gz/2upZdArH97ZvD2LBFfj77lFmAhqAi3wCRlN+ekuYxaZ
+t1pBV9yXig8Dyldg1d7X8pOn2kyrF3rQUDDf4pa7x9vpnbkUlEUifoV9gnYsmdni
+qDzYBtTv2g6MKqwQySXaIUW0YOBPbOellWEwxJqGYQ7y4IfVHfM0iyHnehk2tZcr
++XazLnwGe+Bz4vcguFhJXLyIu//lAOhZtbk6r1QJEUuxaOOQX3wzyceE6nkDsgmr
+P5dj3Zpd7fS2VV2vyGHIFnBJ88LRxreVvgr6Q28UT27SB82zMb7mRZTVE2zeuubT
+5D2D1XbZ0wBo6WiK6eRRrDQ2Haeetkj/uoRy6PWXwnAaTmmIrrXwLqaoJh/U1e+D
+tfsDLWd6IxLjfXvGglrHsrtAz0oprpixUTeVhgTrGk9IQRd5rvxuGUYhFujVaYI6
++QUf+33AFdtncb8y9C9jZmgx8AKbJk+e73SLhB5JVos+WteU7b8d/Mim5mALjnO6
+Z1n/uimsT79sSDqy3XSymtKWXo/22UlrvGCpoEuELPMb6dSFWR7vwrsvhFngY4/K
+UnitnvxboEflQnaIQ4IfRLRzZsX+sC5Esqw9U5tHt4oI+91Dv3KbdbcERgV73K6B
+ZQgC4lkAQquFXiZ5AICkxjiMyZwTtU9KJ7xv17Xu6oywF/3AtbVGETW1D+3maHsD
+y3DASWojyqZdLj+WGzKQRa+swgCDAYKeek2fIAXFSdF63zxJ2RxOJ4GijSaoh+mr
+4HVvcpDaTj+A8T1+QdByM4s98gu4GD7kVtVQGBZdWjutyHvh0hWv1gtVmbhQ/413
+gDMFFDzHIjLTYGYes4hHL22169jVR9sZ1eQxwvTIg3N4pD5cFm0rRuZZTS+oJToF
+G27aBFihAoICAQDyVB62ZDnbxQthk+zITKIzRUrJbLoXrUcANcSHfaN7inF87Ova
+ze7ejT9DNSEhbtfZFJ1G6diOYoSw+2MzFXv0gEkLKY0dETydKgHEu6nVq5eivMgv
+D4hc9YkJMHDSlmv2FDkpL3AXCAmnW9rKp+ddttBZECnmlPEpHLoj6xgBw3pNa1Xs
+IcLVfdugH86Hexj6o0oKgYfcqrX8UUHtUI2/XQqgFrIj8ksjf1fFVWJRJFWmBXqp
+nMEsYarzATeM1kQ/kDeT1ZUpoGPQt02/XqXT4B5A3ATiEtpM2u+l48xtogWWg2Ry
+G9l938StAmhUiW1m7GnKE6EIFvQY85WvbzxOR0JYVUSr7MrasF6nnQlhYxFuIJoJ
+2h/KJQao5GCTvG4+GtbJJm4c2nyZgwyhizMsdgsdcls79aXiMkrZZkamLVUZWOtE
+3pA/oBuz2qnO9HwjbH1HGOccq0TXfmpFScEV3CQGYJdno6Fy7cbmupaL4U9agQ4e
+w+ygL18nq5HV++LStFnVrgs5YijjskfRdE9GUMVDh5pCsd9Y23Fymaad4O/2SRCC
+YkSsyH5OvyDOLpoyUJ6g6Q+45Hqm/3lG4YjNpzFUiMcnp7+3xU35qC0LK8xEfeei
+Ms1mTVEiHNIp6xH/TqRdX73WD7+YuKZSLIfRG7dgrirU6w+mhhvxD51uHQKCAgEA
+2/1mBCR5qm3/0Lt++RQbeyE3tiw40UeyQqucG/+VvY77sSLkI/Lx8iwRlywXcLBn
++A4TvgukmAdWzCs8ndgKNxPA+gfohvBsMOGN9KOB1Ug5vvg2J2kiI64vwYCwzhdZ
+NTUUmL+GMFHUqSsWYg6i7iBFcZmznr4W2T3bBxyTMZki7JStB86e35KXrzc2/W/b
++/p5U2HCSazDHI5mMyuClHc6GmUSVJ7f7LHjL94jviNqobp0Vj603tScHISmNrZw
+TBavkvZGYXsoWKvqavk7jBB9QzaBL+unaFRslg5jTaiKnISj44Us1fjFKu84xifL
+nJaEzjDPt7PBxko7LPgEY7wF39nM9VpoetI7bwR6NwDLSX8UU97MGd+HY+MO1Wi1
+pd2Lapwrx/EK7Oxz335VRK4Je0aZna4j2TyQdMJac9fsGPXv4ZsLfDLj/wD6l1j+
+lLLbBv3ImdSj32LBbhsgF4iCGeXO8HpPO+Q/h9XVsnY52Um2XdNMn03PCGm6ZvtM
+7DXiS+lPF90HjolJVHZTBNtdVRrLr53zLuWEfqT4FeKrDaxdtiXkxLjrB+5/VYu7
+ntyk01ZQ63VNfEwS1irmKl9+qZkTHk3HHV9jNV5RzWViwmJI7Wpr1YzBwmcKCB1O
+oGUADDs8QpnkCz0xkMVtYwHj9qKZlqfbHzrFDUUcF8kCggIAdYvUcgjf//ju8mA8
+5VQ3AcPE6TvycPW+kR2DvW12VcDsF/sc1UA7dHzziPhGn98SmNxlBjb8suSbFPZ8
+QhVT0WBBDkcTilwIGPx9ax7U3S6lGW2VdS6FqQH5fRmgQKZyrCVXLOEz8BgYBrSJ
+xu/3TQAWxH0QtibdbGHg8Pdi58gYlWFRhn9B8Slh1aRYHGPb1AhNLBd0/ddY+5G2
+9xSyDXdmZg1cUA+B3zAwNSqbzFxhp2zU+V1uXsbpk4KtnYV6CZM9QlrCRjTk9iNU
+dVXF/qaiRjfzrm4SsmEpCkEbsrp7F22Y1bkooORglMOsNAWNqfVXw4wN+syXj1ro
+6vZ8PERYrFyAOR1dsQMIhymnmTPjCpaJ4emKrhWTy20sY71thHakZWJc22YoNpbZ
+E6tgIVsJPTlxg/4+fyCCKj5wWr92nhsB1KBZPGO/zFhvMlJpvQ0tH8W2pbN2a0mI
+5x9FqALm/qjwCHfZItSwPM+ZozSht3cOkGHdcD5KXAXfcfsDJc4SHZKVIzq4NusN
+504R/jvD1GP8sglyG7omp75ckgzAmakLdxOP2HhQvIX9tcXpSirNJ6Sl2bwKuuMF
+wxo3r/o/9Y97e4LlfpEYp9eqMdcG+NpR993IwK0UhAWS9H5wdnWBSUHd5e4xtDUt
+iILNRuO46g7R/AIhz1cSSraWWQkCggIBAMhhPP5C9yt9PIm1b0eTwCBctnFSQIKo
+KsA9rll2ab+bMLk9jc8M6MLszy0CtWso09sHf4YY9tifvrkEHRethEh8zscwUuYu
+sm2n1fTixk0ul6LSVgl54uXbMJayENn4PIKRkew8cA8tSma43497w37hmD+MgCb1
+ALzqcco9hfmkgkI6fo1g8Ce3UEECKy2YKSmREdgYcK9JFQO61W6AkFWJcDxAmfzI
+JjFkKwsb7TSw79zWiEdSoM9jm7sCPKATd6Bm/ZAAkUUTuEFkfobn9Ax1rJN/Xxb2
+MKuAUtQv0NYY0gEVdG62jItuKLId6nncH8PG+rsRjPLIYpWqYdJpKx5pUnR+4AkQ
+S6CsRASwcF4PdBvDDBIFG6XpjFo4pPdQhDzL2sTF8b8SWSBLlJQbb7G6UNqgCSau
+SusCFpazvU5NfDmUMuctob2EYVaSXq9jGaj6bTUmDwXHwWilfIk9XfLxnYfXYrJ6
+xhdIpXGmHhuLQtAgK2O1JtLoPc9s9qP8/SkfP7xjjG6xHsP/WvL7QE1pPs9ZM/UI
+C01JNHFi9LKCn8o5mbZjN8jUowi7ffK+76wZUG1L7zM5ytWQOYwo0TQBfc8fpmFw
++RBRJX2kJyDO27ExczoGOKjwqEDaODIB9+9zcCK0BgSoRibSm4ZBvoxzWWD65Kls
+xdPhZUHcFGW5AoICAQC8iG27aD8aRUt94Oek66gFOJx84QVZehWPqtZjWyVenDuc
+T8dink8oejGjcK2UJuQDa83azv90ocVqE0n0ronYyszt9Ib1jlYC+CK1Ar9TYGFg
+WU5OWEDyCzCpqW/w/aG68U8qhKm0MvkLJR+G6evan9TwEhFEVAm3iWllNXs9x29s
+BucwyMMC23zsimxYlS7dA4DtyvVA+zL1omLpSWHbU/qtuI3HV1NeJzsy+gC4mwPh
+j52tdl669fyWLzHzBRLeq6dVOedjnCo+jlU3dL20DEk9SaW08D1CPuZekV1jVPMw
+JoaDcIRh4KLtQ0BYZ7UJeFUTsx1CS/+UqzqYSPOi57a5kvr0Y8YwRnSB8dHVFttX
+JTv83wTQXHPFSBgfnHNe7lsRTfIQfuIkr2bpiU7h85UQ7LsqcI6YHaC07URcsGFF
+FrLWGh91qzAd1diSHla2RnY3n8PPuMnCkguNhLUrYdmyMol7FfWFa9lwplsuTzBq
+B6yj8iaiE3LL+Q/eulJ7S6QPfAI2bU0UJO23Y4koeoIibEEDMSCQ6KYZ2NClRRRT
+ga5fS1YfkDFEcHUQ1/KIkdYHGBKBjoKGExzi8+CgiSySVSYDZl6wIOhLjH2OZ3ol
+ldPN7iNAHirrxg9v8QO6OQlpLUk5Lhp/1dSlZ6sy3UjFqvax3tw6ZjrL88YP5g==
+-----END RSA PRIVATE KEY-----
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
new file mode 100644
index 0000000..c8605d7
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/properties.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_avb/fs_avb_util.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::VBMetaData;
+using android::fs_mgr::VBMetaVerifyResult;
+
+namespace fs_avb_device_test {
+
+// system vbmeta might not be at the end of /system when dynamic partition is
+// enabled. Therefore, disable it by default.
+TEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {
+ Fstab fstab;
+ EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+ FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+ EXPECT_NE(nullptr, system_entry);
+
+ std::string out_public_key_data;
+ std::string out_avb_partition_name;
+ VBMetaVerifyResult out_verify_result;
+ std::unique_ptr<VBMetaData> vbmeta =
+ LoadAndVerifyVbmeta(*system_entry, "" /* expected_public_key_blob */,
+ &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+ EXPECT_NE(nullptr, vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+ EXPECT_EQ("system", out_avb_partition_name);
+ EXPECT_NE("", out_public_key_data);
+}
+
+TEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {
+ // Non-A/B device doesn't have system_other partition.
+ if (fs_mgr_get_slot_suffix() == "") return;
+
+ // Skip running this test if system_other is a logical partition.
+ // Note that system_other is still a physical partition on "retrofit" devices.
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+ return;
+ }
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+ // It should have two lines in the fstab, the first for logical system_other,
+ // the other for physical system_other.
+ EXPECT_EQ(2UL, fstab.size());
+
+ // Use the 2nd fstab entry, which is for physical system_other partition.
+ FstabEntry* system_other = &fstab[1];
+ EXPECT_NE(nullptr, system_other);
+
+ std::string out_public_key_data;
+ std::string out_avb_partition_name;
+ VBMetaVerifyResult out_verify_result;
+ std::unique_ptr<VBMetaData> system_other_vbmeta =
+ LoadAndVerifyVbmeta(*system_other, "" /* expected_public_key_blob */,
+ &out_public_key_data, &out_avb_partition_name, &out_verify_result);
+
+ EXPECT_NE(nullptr, system_other_vbmeta);
+ EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);
+ EXPECT_EQ("system_other", out_avb_partition_name);
+ EXPECT_NE("", out_public_key_data);
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor(out_avb_partition_name, std::move(*system_other_vbmeta));
+ EXPECT_NE(nullptr, hashtree_desc);
+}
+
+TEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {
+ // Non-A/B device doesn't have system_other partition.
+ if (fs_mgr_get_slot_suffix() == "") return;
+
+ // Skip running this test if system_other is a logical partition.
+ // Note that system_other is still a physical partition on "retrofit" devices.
+ if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false)) {
+ return;
+ }
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile("/system/etc/fstab.postinstall", &fstab));
+
+ // It should have two lines in the fstab, the first for logical system_other,
+ // the other for physical system_other.
+ EXPECT_EQ(2UL, fstab.size());
+
+ // Use the 2nd fstab entry, which is for physical system_other partition.
+ FstabEntry* system_other_entry = &fstab[1];
+ // Assign the default key if it's not specified in the fstab.
+ if (system_other_entry->avb_keys.empty()) {
+ system_other_entry->avb_keys = "/system/etc/security/avb/system_other.avbpubkey";
+ }
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);
+ EXPECT_NE(nullptr, avb_handle) << "Failed to load system_other vbmeta. Try 'adb root'?";
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+}
+
+TEST(AvbHandleTest, GetSecurityPatchLevel) {
+ Fstab fstab;
+ EXPECT_TRUE(ReadDefaultFstab(&fstab));
+
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();
+ EXPECT_NE(nullptr, avb_handle) << "Failed to load inline vbmeta. Try 'adb root'?";
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).
+ FstabEntry* system_entry = GetEntryForMountPoint(&fstab, "/system");
+ EXPECT_NE(nullptr, system_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());
+
+ FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, "/vendor");
+ EXPECT_NE(nullptr, vendor_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());
+
+ FstabEntry* product_entry = GetEntryForMountPoint(&fstab, "/product");
+ EXPECT_NE(nullptr, product_entry);
+ EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());
+}
+
+} // namespace fs_avb_device_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
new file mode 100644
index 0000000..2c819a9
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <fs_avb/fs_avb.h>
+#include <libavb/libavb.h>
+
+#include "fs_avb_test_util.h"
+
+// Target classes or functions to test:
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::HashAlgorithm;
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbTest : public BaseFsAvbTest {
+ public:
+ PublicFsAvbTest(){};
+
+ protected:
+ ~PublicFsAvbTest(){};
+ // Modifies |flags| field in the vbmeta header in an Avb image.
+ // e.g., AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.
+ void ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path, uint32_t flags);
+};
+
+void PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path,
+ uint32_t flags) {
+ if (!base::PathExists(vbmeta_image_path)) return;
+
+ // Only support modifying the flags in vbmeta*.img.
+ std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();
+ ASSERT_TRUE(base::StartsWith(image_file_name, "vbmeta", base::CompareCase::INSENSITIVE_ASCII));
+
+ android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));
+ EXPECT_TRUE(fd > 0);
+
+ auto flags_offset = offsetof(AvbVBMetaImageHeader, flags);
+ uint32_t flags_data = htobe32(flags);
+ EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));
+ EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmeta) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Invokes the public API from fs_avb.h.
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Checks the summary info for all vbmeta images.
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+ // Skip loading chained vbmeta.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, false /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+ EXPECT_EQ("5c31197992b3c72a854ec7dc0eb9609ffebcffab7917ffd381a99ecee328f09c",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithModifications) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ // Calculates the digest of all chained partitions, to ensure the chained is formed properly.
+ EXPECT_EQ("abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+
+ // Sets AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED in the vbmeta.img.
+ ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ // Returns a null handler because allow_verification is not True.
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Try again with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kHashtreeDisabled, avb_handle->status());
+
+ // Checks the summary info for all vbmeta images.
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ("ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+
+ // Sets AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED in the vbmeta.img.
+ ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);
+ // Loads the vbmeta with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationDisabled, avb_handle->status());
+ // Only the top-level vbmeta.img is loaded, when VERIFICATION_DISABLED is set.
+ // However, CalcVBMetaDigest() reads all vbmeta structs to calculate the digest,
+ // including vbmeta.img, boot.img and syste.img. So we don't compare the digest here.
+ EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);
+
+ // Sets a unknown flag in the vbmeta.imgm and expects to get
+ // AvbHandleStatus::kVerificationError.
+ ModifyVBMetaHeaderFlags(vbmeta_path, 0x10000000);
+ // Loads the vbmeta with allow_verification_error set to true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,
+ true /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+ // Checks the digest matches the value calculated by CalcVBMetaDigest().
+ EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+ CalcVBMetaDigest("vbmeta.img", "sha256"));
+ EXPECT_EQ("8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1",
+ avb_handle->vbmeta_info().digest);
+ EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);
+ EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);
+}
+
+TEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithPublicKeys) {
+ // Generates a raw boot.img
+ const size_t boot_image_size = 5 * 1024 * 1024;
+ const size_t boot_partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", boot_image_size);
+
+ // Adds AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", boot_partition_size, "SHA256_RSA2048", 10,
+ data_dir_.Append("testkey_rsa2048.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates a raw system.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Generates chain partition descriptors.
+ base::FilePath rsa2048_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa2048.pem"));
+ base::FilePath rsa4096_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa4096.pem"));
+ base::FilePath rsa8192_public_key =
+ ExtractPublicKeyAvb(data_dir_.Append("testkey_rsa8192.pem"));
+
+ // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.
+ auto vbmeta_path = GenerateVBMetaImage("vbmeta.img", "SHA256_RSA8192", 0,
+ data_dir_.Append("testkey_rsa8192.pem"),
+ {}, /* include_descriptor_image_paths */
+ {{"boot", 1, rsa2048_public_key}, /* chain_partitions */
+ {"system", 2, rsa4096_public_key}},
+ "--internal_release_string \"unit test\"");
+
+ auto vbmeta_image_path = [this](const std::string& partition_name) {
+ return test_dir_.Append(partition_name + ".img").value();
+ };
+ std::vector<VBMetaData> vbmeta_images;
+ // Uses the correct expected public key.
+ auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa8192_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());
+
+ // Uses a non-existed public key.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ "/path/to/non-existed/key", HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Uses an incorrect public key, with allow_verification_error false.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa4096_public_key.value(), HashAlgorithm::kSHA256,
+ false /* allow_verification_error */, true /* load_chained_vbmeta */,
+ true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_EQ(nullptr, avb_handle);
+
+ // Uses an incorrect public key, with allow_verification_error true.
+ avb_handle = AvbHandle::LoadAndVerifyVbmeta(
+ "vbmeta" /* partition_name */, "" /* ab_suffix */, "" /* other_suffix */,
+ rsa4096_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,
+ true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);
+ EXPECT_NE(nullptr, avb_handle);
+ EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
new file mode 100644
index 0000000..17f4c4e
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb_test_util.h"
+
+#include <stdlib.h>
+
+#include <android-base/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+
+namespace fs_avb_host_test {
+
+// Need to match the data setting in Android.bp:
+// data: ["tests/data/*"]
+base::FilePath BaseFsAvbTest::data_dir_ = base::FilePath("tests/data");
+
+void BaseFsAvbTest::SetUp() {
+ // Changes current directory to test executable directory so that relative path
+ // references to test dependencies don't rely on being manually run from
+ // the executable directory. With this, we can just open "./tests/data/testkey_rsa2048.pem"
+ // from the source.
+ base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));
+
+ // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+ base::CreateTemporaryDirInDir(tmp_dir, "libfs_avb-tests.", &test_dir_);
+}
+
+void BaseFsAvbTest::TearDown() {
+ // Nukes temporary directory.
+ ASSERT_NE(std::string::npos, test_dir_.value().find("libfs_avb-tests"));
+ ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));
+}
+
+std::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,
+ const std::string& hash_algorithm) {
+ auto iter = vbmeta_images_.find(file_name);
+ EXPECT_NE(iter, vbmeta_images_.end()); // ensures file_name is generated before.
+
+ // Gets the image path from iterator->second.path: VBMetaImage.path.
+ base::FilePath image_path = iter->second.path;
+ base::FilePath vbmeta_digest_path = test_dir_.Append("vbmeta_digest");
+ EXPECT_COMMAND(0,
+ "avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s"
+ " --output %s",
+ image_path.value().c_str(), hash_algorithm.c_str(),
+ vbmeta_digest_path.value().c_str());
+ // Reads the content of the output digest file.
+ std::string vbmeta_digest_data;
+ EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));
+ // Returns the trimmed digest.
+ std::string trimmed_digest_data;
+ base::TrimString(vbmeta_digest_data, " \t\n", &trimmed_digest_data);
+ return trimmed_digest_data;
+}
+
+base::FilePath BaseFsAvbTest::GenerateVBMetaImage(
+ const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+ const base::FilePath& key_path,
+ const std::vector<base::FilePath>& include_descriptor_image_paths,
+ const std::vector<ChainPartitionConfig>& chain_partitions,
+ const std::string& additional_options) {
+ // --algorithm and --key
+ std::string signing_options;
+ if (avb_algorithm == "") {
+ signing_options = " --algorithm NONE ";
+ } else {
+ signing_options =
+ std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+ }
+ // --include_descriptors_from_image
+ std::string include_descriptor_options;
+ for (const auto& path : include_descriptor_image_paths) {
+ include_descriptor_options += " --include_descriptors_from_image " + path.value();
+ }
+ // --chain_partitions
+ std::string chain_partition_options;
+ for (const auto& partition : chain_partitions) {
+ chain_partition_options += base::StringPrintf(
+ " --chain_partition %s:%u:%s", partition.partition_name.c_str(),
+ partition.rollback_index_location, partition.key_blob_path.value().c_str());
+ }
+ // Starts to 'make_vbmeta_image'.
+ VBMetaImage vbmeta_image;
+ vbmeta_image.path = test_dir_.Append(file_name);
+ EXPECT_COMMAND(0,
+ "avbtool make_vbmeta_image"
+ " --rollback_index %" PRIu64
+ " %s %s %s %s"
+ " --output %s",
+ rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),
+ chain_partition_options.c_str(), additional_options.c_str(),
+ vbmeta_image.path.value().c_str());
+ int64_t file_size;
+ EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+ vbmeta_image.content.resize(file_size);
+ EXPECT_TRUE(base::ReadFile(vbmeta_image.path,
+ reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+ // Stores the generated vbmeta image into vbmeta_images_ member object.
+ vbmeta_images_.emplace(file_name, std::move(vbmeta_image));
+
+ return vbmeta_images_[file_name].path; // returns the path.
+}
+
+base::FilePath BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,
+ const std::string& output_file_name,
+ const size_t padding_size) {
+ VBMetaImage vbmeta_image;
+ vbmeta_image.path = test_dir_.Append(output_file_name);
+ EXPECT_COMMAND(0,
+ "avbtool extract_vbmeta_image"
+ " --image %s"
+ " --output %s"
+ " --padding_size %zu",
+ image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);
+ int64_t file_size;
+ EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));
+ vbmeta_image.content.resize(file_size);
+ EXPECT_TRUE(base::ReadFile(vbmeta_image.path,
+ reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));
+ // Stores the extracted vbmeta image into vbmeta_images_ member object.
+ vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));
+
+ // Returns the output file path.
+ return vbmeta_images_[output_file_name].path;
+}
+
+// Generates a file with name |file_name| of size |image_size| with
+// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+base::FilePath BaseFsAvbTest::GenerateImage(const std::string& file_name, size_t image_size,
+ uint8_t start_byte) {
+ std::vector<uint8_t> image;
+ image.resize(image_size);
+ for (size_t n = 0; n < image_size; n++) {
+ image[n] = uint8_t(n + start_byte);
+ }
+ base::FilePath image_path = test_dir_.Append(file_name);
+ EXPECT_EQ(image_size,
+ static_cast<const size_t>(base::WriteFile(
+ image_path, reinterpret_cast<const char*>(image.data()), image.size())));
+ return image_path;
+}
+
+void BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+ const std::string& partition_name, const uint64_t partition_size,
+ const std::string& avb_algorithm, uint64_t rollback_index,
+ const base::FilePath& key_path, const std::string& salt,
+ const std::string& additional_options) {
+ // 'add_hash_footer' or 'add_hashtree_footer'.
+ EXPECT_TRUE(footer_type == "hash" or footer_type == "hashtree");
+ std::string add_footer_option = "add_" + footer_type + "_footer";
+
+ std::string signing_options;
+ if (avb_algorithm == "") {
+ signing_options = " --algorithm NONE ";
+ } else {
+ signing_options =
+ std::string(" --algorithm ") + avb_algorithm + " --key " + key_path.value() + " ";
+ }
+ EXPECT_COMMAND(0,
+ "avbtool %s"
+ " --image %s"
+ " --partition_name %s "
+ " --partition_size %" PRIu64 " --rollback_index %" PRIu64
+ " --salt %s"
+ " %s %s",
+ add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),
+ partition_size, rollback_index, salt.c_str(), signing_options.c_str(),
+ additional_options.c_str());
+}
+
+VBMetaData BaseFsAvbTest::GenerateImageAndExtractVBMetaData(
+ const std::string& partition_name, const size_t image_size, const size_t partition_size,
+ const std::string& footer_type, const base::FilePath& avb_signing_key,
+ const std::string& avb_algorithm, const uint64_t rollback_index) {
+ // Generates a raw image first
+ base::FilePath image_path = GenerateImage(partition_name + ".img", image_size);
+
+ // Appends AVB Hashtree Footer.
+ AddAvbFooter(image_path, footer_type, partition_name, partition_size, avb_algorithm,
+ rollback_index, avb_signing_key, "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ // Extracts vbmeta from the ram image into another *-vbmeta.img.
+ auto vbmeta_image = ExtractVBMetaImage(image_path, partition_name + "-vbmeta.img");
+
+ // Loads *-vbmeta.img into a VBMetaData.
+ std::string vbmeta_buffer;
+ EXPECT_TRUE(base::ReadFileToString(vbmeta_image, &vbmeta_buffer));
+
+ return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::LoadVBMetaData(const std::string& file_name) {
+ auto iter = vbmeta_images_.find(file_name);
+ EXPECT_NE(iter, vbmeta_images_.end()); // ensures file_name is generated before.
+
+ // Gets the image path from iterator->second.path: VBMetaImage.path.
+ base::FilePath image_path = iter->second.path;
+
+ // Loads the vbmeta_image into a VBMetaData.
+ std::string vbmeta_buffer;
+ EXPECT_TRUE(base::ReadFileToString(image_path, &vbmeta_buffer));
+
+ std::string partition_name = image_path.RemoveExtension().BaseName().value();
+ return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};
+}
+
+VBMetaData BaseFsAvbTest::ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+ const std::string& output_file_name) {
+ ExtractVBMetaImage(image_path, output_file_name);
+ return LoadVBMetaData(output_file_name);
+}
+
+std::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {
+ base::FilePath tmp_path = test_dir_.Append("info_output.txt");
+ EXPECT_COMMAND(0, "avbtool info_image --image %s --output %s", image_path.value().c_str(),
+ tmp_path.value().c_str());
+ std::string info_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));
+ return info_data;
+}
+
+std::string BaseFsAvbTest::InfoImage(const std::string& file_name) {
+ auto iter = vbmeta_images_.find(file_name);
+ EXPECT_NE(iter, vbmeta_images_.end()); // ensures file_name is generated before.
+ // Gets the image path from iterator->second.path: VBMetaImage.path.
+ base::FilePath image_path = iter->second.path;
+ return InfoImage(image_path);
+}
+
+base::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {
+ std::string file_name = key_path.RemoveExtension().BaseName().value();
+ base::FilePath tmp_path = test_dir_.Append(file_name + "public_key.bin");
+ EXPECT_COMMAND(0,
+ "avbtool extract_public_key --key %s"
+ " --output %s",
+ key_path.value().c_str(), tmp_path.value().c_str());
+ return tmp_path;
+}
+
+std::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {
+ base::FilePath tmp_path = test_dir_.Append("public_key.bin");
+ EXPECT_COMMAND(0,
+ "avbtool extract_public_key --key %s"
+ " --output %s",
+ key_path.value().c_str(), tmp_path.value().c_str());
+ std::string key_data;
+ EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));
+ return key_data;
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_test_util.h b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
new file mode 100644
index 0000000..ab1980b
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_test_util.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <base/files/file_path.h>
+#include <base/strings/stringprintf.h>
+#include <fs_avb/types.h>
+#include <gtest/gtest.h>
+
+// Utility macro to run the command expressed by the printf()-style string
+// |command_format| using the system(3) utility function. Will assert unless
+// the command exits normally with exit status |expected_exit_status|.
+#define EXPECT_COMMAND(expected_exit_status, command_format, ...) \
+ do { \
+ int rc = system(base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \
+ EXPECT_TRUE(WIFEXITED(rc)); \
+ EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status); \
+ } while (0);
+
+using android::fs_mgr::VBMetaData;
+
+namespace fs_avb_host_test {
+
+struct VBMetaImage {
+ // Path to vbmeta image generated with GenerateVBMetaImage().
+ base::FilePath path;
+ // Contents of the image generated with GenerateVBMetaImage().
+ std::vector<uint8_t> content;
+};
+
+struct ChainPartitionConfig {
+ std::string partition_name;
+ uint32_t rollback_index_location;
+ base::FilePath key_blob_path;
+};
+
+inline android::base::unique_fd OpenUniqueReadFd(const base::FilePath& file_path) {
+ return android::base::unique_fd(open(file_path.value().c_str(), O_RDONLY | O_CLOEXEC));
+}
+
+/* Base-class used for unit test. */
+class BaseFsAvbTest : public ::testing::Test {
+ public:
+ BaseFsAvbTest() {}
+
+ protected:
+ virtual ~BaseFsAvbTest() {}
+
+ // Calculates the vbmeta digest using 'avbtool calc_vbmeta_digest' command.
+ // Note that the calculation includes chained vbmeta images.
+ std::string CalcVBMetaDigest(const std::string& file_name, const std::string& hash_algorithm);
+
+ // Generates a vbmeta image with |file_name| by avbtool.
+ // The generated vbmeta image will be written to disk, see the
+ // |vbmeta_images_| variable for its path and the content.
+ base::FilePath GenerateVBMetaImage(
+ const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,
+ const base::FilePath& key_path,
+ const std::vector<base::FilePath>& include_descriptor_image_paths,
+ const std::vector<ChainPartitionConfig>& chain_partitions,
+ const std::string& additional_options = "");
+ // Similar to above, but extracts a vbmeta image from the given image_path.
+ // The extracted vbmeta image will be written to disk, with |output_file_name|.
+ // See the |vbmeta_images_| variable for its path and the content.
+ base::FilePath ExtractVBMetaImage(const base::FilePath& image_path,
+ const std::string& output_file_name,
+ const size_t padding_size = 0);
+
+ // Generate a file with name |file_name| of size |image_size| with
+ // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).
+ base::FilePath GenerateImage(const std::string& file_name, size_t image_size,
+ uint8_t start_byte = 0);
+ // Invokes 'avbtool add_hash_footer' or 'avbtool add_hashtree_footer' to sign
+ // the |image_path|. The |footer_type| can be either "hash" or "hashtree".
+ void AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,
+ const std::string& partition_name, const uint64_t partition_size,
+ const std::string& avb_algorithm, uint64_t rollback_index,
+ const base::FilePath& avb_signing_key, const std::string& salt = "d00df00d",
+ const std::string& additional_options = "");
+
+ VBMetaData GenerateImageAndExtractVBMetaData(
+ const std::string& partition_name, const size_t image_size, const size_t partition_size,
+ const std::string& footer_type, const base::FilePath& avb_signing_key,
+ const std::string& avb_algorithm, const uint64_t rollback_index);
+
+ VBMetaData ExtractAndLoadVBMetaData(const base::FilePath& image_path,
+ const std::string& output_file_name);
+
+ VBMetaData LoadVBMetaData(const std::string& file_name);
+
+ // Returns the output of 'avbtool info_image' for the |image_path|.
+ std::string InfoImage(const base::FilePath& image_path);
+ // Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.
+ std::string InfoImage(const std::string& file_name);
+
+ // Extracts public key blob in AVB format for a .pem key, then returns the
+ // file path: a .bin file.
+ base::FilePath ExtractPublicKeyAvb(const base::FilePath& key_path);
+ // Same as above, but returns the key blob binary instead.
+ std::string ExtractPublicKeyAvbBlob(const base::FilePath& key_path);
+
+ void SetUp() override;
+ void TearDown() override;
+
+ // Temporary directory created in SetUp().
+ base::FilePath test_dir_;
+ // Maps vbmeta image name (e.g., vbmeta_a.img, system_a.img) to VBMetaImage.
+ std::map<std::string, VBMetaImage> vbmeta_images_;
+
+ static base::FilePath data_dir_;
+};
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
new file mode 100644
index 0000000..7c34009
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fs_avb/fs_avb_util.h>
+
+#include "fs_avb_test_util.h"
+
+namespace fs_avb_host_test {
+
+class PublicFsAvbUtilTest : public BaseFsAvbTest {
+ public:
+ PublicFsAvbUtilTest(){};
+
+ protected:
+ ~PublicFsAvbUtilTest(){};
+};
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {
+ // Generates a raw system_other.img, use a smaller size to speed-up unit test.
+ const size_t system_image_size = 10 * 1024 * 1024;
+ const size_t system_partition_size = 15 * 1024 * 1024;
+ base::FilePath system_path = GenerateImage("system.img", system_image_size);
+
+ // Adds AVB Hashtree Footer.
+ AddAvbFooter(system_path, "hashtree", "system", system_partition_size, "SHA512_RSA4096", 20,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+
+ auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, "system-vbmeta.img");
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor("system" /* avb_partition_name */, std::move(system_vbmeta));
+ EXPECT_NE(nullptr, hashtree_desc);
+
+ // Checks the returned hashtree_desc matches the following info returned by avbtool.
+ EXPECT_EQ(
+ "Footer version: 1.0\n"
+ "Image size: 15728640 bytes\n"
+ "Original image size: 10485760 bytes\n"
+ "VBMeta offset: 10661888\n"
+ "VBMeta size: 2112 bytes\n"
+ "--\n"
+ "Minimum libavb version: 1.0\n"
+ "Header Block: 256 bytes\n"
+ "Authentication Block: 576 bytes\n"
+ "Auxiliary Block: 1280 bytes\n"
+ "Algorithm: SHA512_RSA4096\n"
+ "Rollback Index: 20\n"
+ "Flags: 0\n"
+ "Release String: 'unit test'\n"
+ "Descriptors:\n"
+ " Hashtree descriptor:\n"
+ " Version of dm-verity: 1\n"
+ " Image Size: 10485760 bytes\n"
+ " Tree Offset: 10485760\n"
+ " Tree Size: 86016 bytes\n"
+ " Data Block Size: 4096 bytes\n"
+ " Hash Block Size: 4096 bytes\n"
+ " FEC num roots: 2\n"
+ " FEC offset: 10571776\n"
+ " FEC size: 90112 bytes\n"
+ " Hash Algorithm: sha1\n"
+ " Partition Name: system\n"
+ " Salt: d00df00d\n"
+ " Root Digest: a3d5dd307341393d85de356c384ff543ec1ed81b\n"
+ " Flags: 0\n",
+ InfoImage(system_path));
+
+ EXPECT_EQ(1UL, hashtree_desc->dm_verity_version);
+ EXPECT_EQ(10485760UL, hashtree_desc->image_size);
+ EXPECT_EQ(10485760UL, hashtree_desc->tree_offset);
+ EXPECT_EQ(86016UL, hashtree_desc->tree_size);
+ EXPECT_EQ(4096UL, hashtree_desc->data_block_size);
+ EXPECT_EQ(4096UL, hashtree_desc->hash_block_size);
+ EXPECT_EQ(2UL, hashtree_desc->fec_num_roots);
+ EXPECT_EQ(10571776UL, hashtree_desc->fec_offset);
+ EXPECT_EQ(90112UL, hashtree_desc->fec_size);
+ EXPECT_EQ(std::string("sha1"),
+ std::string(reinterpret_cast<const char*>(hashtree_desc->hash_algorithm)));
+ EXPECT_EQ(std::string("system").length(), hashtree_desc->partition_name_len);
+ EXPECT_EQ(hashtree_desc->partition_name, "system");
+ EXPECT_EQ(hashtree_desc->salt, "d00df00d");
+ EXPECT_EQ(hashtree_desc->root_digest, "a3d5dd307341393d85de356c384ff543ec1ed81b");
+
+ // Checks it's null if partition name doesn't match.
+ EXPECT_EQ(nullptr, GetHashtreeDescriptor("system_not_exist" /* avb_partition_name */,
+ std::move(system_vbmeta)));
+}
+
+TEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor_NotFound) {
+ // Generates a raw boot.img
+ const size_t image_size = 5 * 1024 * 1024;
+ const size_t partition_size = 10 * 1024 * 1024;
+ base::FilePath boot_path = GenerateImage("boot.img", image_size);
+ // Appends AVB Hash Footer.
+ AddAvbFooter(boot_path, "hash", "boot", partition_size, "SHA256_RSA4096", 10,
+ data_dir_.Append("testkey_rsa4096.pem"), "d00df00d",
+ "--internal_release_string \"unit test\"");
+ // Extracts boot vbmeta from boot.img into boot-vbmeta.img.
+ auto boot_vbmeta = ExtractAndLoadVBMetaData(boot_path, "boot-vbmeta.img");
+
+ auto hashtree_desc =
+ GetHashtreeDescriptor("boot" /* avb_partition_name */, std::move(boot_vbmeta));
+ EXPECT_EQ(nullptr, hashtree_desc);
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/tests/util_test.cpp b/fs_mgr/libfs_avb/tests/util_test.cpp
new file mode 100644
index 0000000..12b5acb
--- /dev/null
+++ b/fs_mgr/libfs_avb/tests/util_test.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+
+#include <future>
+#include <string>
+#include <thread>
+
+#include <base/files/file_util.h>
+
+#include "fs_avb_test_util.h"
+#include "util.h"
+
+// Target functions to test:
+using android::fs_mgr::BytesToHex;
+using android::fs_mgr::FileWaitMode;
+using android::fs_mgr::HexToBytes;
+using android::fs_mgr::NibbleValue;
+using android::fs_mgr::WaitForFile;
+
+namespace fs_avb_host_test {
+
+TEST(BasicUtilTest, NibbleValue09) {
+ uint8_t value;
+
+ EXPECT_TRUE(NibbleValue('0', &value));
+ EXPECT_EQ(0, value);
+ EXPECT_TRUE(NibbleValue('1', &value));
+ EXPECT_EQ(1, value);
+ EXPECT_TRUE(NibbleValue('2', &value));
+ EXPECT_EQ(2, value);
+ EXPECT_TRUE(NibbleValue('3', &value));
+ EXPECT_EQ(3, value);
+ EXPECT_TRUE(NibbleValue('4', &value));
+ EXPECT_EQ(4, value);
+ EXPECT_TRUE(NibbleValue('5', &value));
+ EXPECT_EQ(5, value);
+ EXPECT_TRUE(NibbleValue('6', &value));
+ EXPECT_EQ(6, value);
+ EXPECT_TRUE(NibbleValue('7', &value));
+ EXPECT_EQ(7, value);
+ EXPECT_TRUE(NibbleValue('8', &value));
+ EXPECT_EQ(8, value);
+ EXPECT_TRUE(NibbleValue('9', &value));
+ EXPECT_EQ(9, value);
+}
+
+TEST(BasicUtilTest, NibbleValueAF) {
+ uint8_t value;
+
+ EXPECT_TRUE(NibbleValue('a', &value));
+ EXPECT_EQ(10, value);
+ EXPECT_TRUE(NibbleValue('b', &value));
+ EXPECT_EQ(11, value);
+ EXPECT_TRUE(NibbleValue('c', &value));
+ EXPECT_EQ(12, value);
+ EXPECT_TRUE(NibbleValue('d', &value));
+ EXPECT_EQ(13, value);
+ EXPECT_TRUE(NibbleValue('e', &value));
+ EXPECT_EQ(14, value);
+ EXPECT_TRUE(NibbleValue('f', &value));
+ EXPECT_EQ(15, value);
+
+ EXPECT_TRUE(NibbleValue('A', &value));
+ EXPECT_EQ(10, value);
+ EXPECT_TRUE(NibbleValue('B', &value));
+ EXPECT_EQ(11, value);
+ EXPECT_TRUE(NibbleValue('C', &value));
+ EXPECT_EQ(12, value);
+ EXPECT_TRUE(NibbleValue('D', &value));
+ EXPECT_EQ(13, value);
+ EXPECT_TRUE(NibbleValue('E', &value));
+ EXPECT_EQ(14, value);
+ EXPECT_TRUE(NibbleValue('F', &value));
+ EXPECT_EQ(15, value);
+}
+
+TEST(BasicUtilTest, NibbleValueInvalid) {
+ uint8_t value;
+
+ EXPECT_FALSE(NibbleValue('G', &value));
+ EXPECT_FALSE(NibbleValue('H', &value));
+ EXPECT_FALSE(NibbleValue('I', &value));
+ EXPECT_FALSE(NibbleValue('x', &value));
+ EXPECT_FALSE(NibbleValue('y', &value));
+ EXPECT_FALSE(NibbleValue('z', &value));
+}
+
+TEST(BasicUtilTest, HexToBytes) {
+ std::string hex = "000102030405060708090A0B0C0D0E0F";
+ uint8_t bytes[16];
+
+ EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+ for (size_t i = 0; i < sizeof(bytes); i++) {
+ EXPECT_EQ(i, bytes[i]);
+ }
+}
+
+TEST(BasicUtilTest, HexToBytes2) {
+ std::string hex = "101112131415161718191A1B1C1D1E1F";
+ uint8_t bytes[16];
+
+ EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+ for (size_t i = 0; i < sizeof(bytes); i++) {
+ EXPECT_EQ(16 + i, bytes[i]);
+ }
+}
+
+TEST(BasicUtilTest, BytesToHex) {
+ const uint8_t bytes[16]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+
+ EXPECT_EQ("0102", BytesToHex((uint8_t*)bytes, 2));
+ EXPECT_EQ("01020304", BytesToHex((uint8_t*)bytes, 4));
+ EXPECT_EQ("0102030405060708", BytesToHex((uint8_t*)bytes, 8));
+ EXPECT_EQ("0102030405060708090a0b0c0d0e0f10", BytesToHex((uint8_t*)bytes, 16));
+
+ EXPECT_EQ("01", BytesToHex((uint8_t*)bytes, 1));
+ EXPECT_EQ("010203", BytesToHex((uint8_t*)bytes, 3));
+ EXPECT_EQ("0102030405", BytesToHex((uint8_t*)bytes, 5));
+}
+
+TEST(BasicUtilTest, HexToBytesInValidOddLenHex) {
+ std::string hex = "12345";
+ uint8_t bytes[16];
+
+ EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, HexToBytesInsufficientByteLen) {
+ std::string hex = "101112131415161718191A1B1C1D1E1F";
+ uint8_t bytes[8];
+
+ EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));
+}
+
+TEST(BasicUtilTest, WaitForFile) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Waits this path.
+ base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+
+ EXPECT_TRUE(base::CreateDirectory(wait_path));
+ EXPECT_TRUE(WaitForFile(wait_path.value(), 1s));
+
+ // Removes the wait_path.
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileNonExist) {
+ base::FilePath wait_path("/path/not/exist");
+ EXPECT_FALSE(WaitForFile(wait_path.value(), 200ms));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreation) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Waits this path.
+ base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+ auto wait_file = std::async(WaitForFile, wait_path.value(), 500ms, FileWaitMode::Exists);
+
+ // Sleeps 100ms before creating the wait_path.
+ std::this_thread::sleep_for(100ms);
+ EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+ // Checks WaitForFile() returns success.
+ EXPECT_TRUE(wait_file.get());
+
+ // Removes the wait_path.
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+TEST(BasicUtilTest, WaitForFileDeferCreationFailure) {
+ // Gets system tmp dir.
+ base::FilePath tmp_dir;
+ ASSERT_TRUE(GetTempDir(&tmp_dir));
+
+ // Waits this path.
+ base::FilePath wait_path = tmp_dir.Append("libfs_avb-test-exist-dir");
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+ auto wait_file = std::async(WaitForFile, wait_path.value(), 50ms, FileWaitMode::Exists);
+
+ // Sleeps 100ms before creating the wait_path.
+ std::this_thread::sleep_for(100ms);
+ EXPECT_TRUE(base::CreateDirectory(wait_path));
+
+ // Checks WaitForFile() returns failure, because it only waits 50ms.
+ EXPECT_FALSE(wait_file.get());
+
+ // Removes the wait_path.
+ ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));
+}
+
+} // namespace fs_avb_host_test
diff --git a/fs_mgr/libfs_avb/types.cpp b/fs_mgr/libfs_avb/types.cpp
new file mode 100644
index 0000000..3c277f3
--- /dev/null
+++ b/fs_mgr/libfs_avb/types.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fs_avb/types.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper functions to print enum class VBMetaVerifyResult.
+const char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {
+ // clang-format off
+ static const char* const name[] = {
+ "ResultSuccess",
+ "ResultError",
+ "ResultErrorVerification",
+ "ResultUnknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(result);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {
+ os << VBMetaVerifyResultToString(result);
+ return os;
+}
+
+// Helper functions to dump enum class AvbHandleStatus.
+const char* AvbHandleStatusToString(AvbHandleStatus status) {
+ // clang-format off
+ static const char* const name[] = {
+ "Success",
+ "Uninitialized",
+ "HashtreeDisabled",
+ "VerificationDisabled",
+ "VerificationError",
+ "Unknown",
+ };
+ // clang-format on
+
+ uint32_t index = static_cast<uint32_t>(status);
+ uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;
+ if (index >= unknown_index) {
+ index = unknown_index;
+ }
+
+ return name[index];
+}
+
+std::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {
+ os << AvbHandleStatusToString(status);
+ return os;
+}
+
+// class VBMetaData
+// ----------------
+std::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {
+ auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();
+
+ if (!vbmeta_header) return nullptr;
+
+ /* Byteswap the header. */
+ avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),
+ vbmeta_header.get());
+ if (update_vbmeta_size) {
+ vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +
+ vbmeta_header->authentication_data_block_size +
+ vbmeta_header->auxiliary_data_block_size;
+ }
+
+ return vbmeta_header;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/util.cpp b/fs_mgr/libfs_avb/util.cpp
new file mode 100644
index 0000000..d214b5b
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+#include <sys/ioctl.h>
+
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include <linux/fs.h>
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value) {
+ CHECK(value != nullptr);
+
+ switch (c) {
+ case '0' ... '9':
+ *value = c - '0';
+ break;
+ case 'a' ... 'f':
+ *value = c - 'a' + 10;
+ break;
+ case 'A' ... 'F':
+ *value = c - 'A' + 10;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {
+ CHECK(bytes != nullptr);
+
+ if (hex.size() % 2 != 0) {
+ return false;
+ }
+ if (hex.size() / 2 > bytes_len) {
+ return false;
+ }
+ for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {
+ uint8_t high;
+ if (!NibbleValue(hex[i], &high)) {
+ return false;
+ }
+ uint8_t low;
+ if (!NibbleValue(hex[i + 1], &low)) {
+ return false;
+ }
+ bytes[j] = (high << 4) | low;
+ }
+ return true;
+}
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) {
+ CHECK(bytes != nullptr);
+
+ static const char* hex_digits = "0123456789abcdef";
+ std::string hex;
+
+ for (size_t i = 0; i < bytes_len; i++) {
+ hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);
+ hex.push_back(hex_digits[bytes[i] & 0x0F]);
+ }
+ return hex;
+}
+
+// TODO: remove duplicate code with fs_mgr_wait_for_file
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,
+ FileWaitMode file_wait_mode) {
+ auto start_time = std::chrono::steady_clock::now();
+
+ while (true) {
+ int rv = access(filename.c_str(), F_OK);
+ if (file_wait_mode == FileWaitMode::Exists) {
+ if (!rv || errno != ENOENT) return true;
+ } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
+ if (rv && errno == ENOENT) return true;
+ }
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
+ }
+}
+
+bool IsDeviceUnlocked() {
+ std::string verified_boot_state;
+
+ if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+ return verified_boot_state == "orange";
+ }
+ return false;
+}
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ return false;
+ }
+
+ int ON = 1;
+ return ioctl(fd, BLKROSET, &ON) == 0;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
new file mode 100644
index 0000000..7763da5
--- /dev/null
+++ b/fs_mgr/libfs_avb/util.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <string>
+
+#ifdef HOST_TEST
+#include <base/logging.h>
+#else
+#include <android-base/logging.h>
+#endif
+
+#define FS_AVB_TAG "[libfs_avb]"
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FS_AVB_TAG
+#define LWARNING LOG(WARNING) << FS_AVB_TAG
+#define LERROR LOG(ERROR) << FS_AVB_TAG
+#define LFATAL LOG(FATAL) << FS_AVB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FS_AVB_TAG
+#define PWARNING PLOG(WARNING) << FS_AVB_TAG
+#define PERROR PLOG(ERROR) << FS_AVB_TAG
+#define PFATAL PLOG(FATAL) << FS_AVB_TAG
+
+extern bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace fs_mgr {
+
+bool NibbleValue(const char& c, uint8_t* value);
+
+bool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex);
+
+std::string BytesToHex(const uint8_t* bytes, size_t bytes_len);
+
+enum class FileWaitMode { Exists, DoesNotExist };
+bool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,
+ FileWaitMode wait_mode = FileWaitMode::Exists);
+
+bool IsDeviceUnlocked();
+
+bool SetBlockDeviceReadOnly(const std::string& blockdev);
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 5689bdf..b504161 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -14,6 +14,16 @@
// limitations under the License.
//
+liblp_lib_deps = [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ "libsparse",
+ "libext4_utils",
+ "libz",
+]
+
cc_library {
name: "liblp",
host_supported: true,
@@ -30,37 +40,43 @@
"utility.cpp",
"writer.cpp",
],
- shared_libs: [
- "libbase",
- "liblog",
- "libcrypto",
- "libcrypto_utils",
- "libsparse",
- "libext4_utils",
- "libz",
- ],
+ shared_libs: liblp_lib_deps,
+ target: {
+ windows: {
+ enabled: true,
+ },
+ android: {
+ shared_libs: [
+ "libcutils",
+ ],
+ },
+ },
export_include_dirs: ["include"],
}
cc_test {
- name: "liblp_test",
+ name: "liblp_test_static",
defaults: ["fs_mgr_defaults"],
cppflags: [
"-Wno-unused-parameter",
],
static_libs: [
"libgmock",
- ],
- shared_libs: [
- "liblp",
- "libbase",
"libfs_mgr",
- "libsparse",
- ],
+ "liblp",
+ ] + liblp_lib_deps,
+ stl: "libc++_static",
srcs: [
"builder_test.cpp",
"io_test.cpp",
"test_partition_opener.cpp",
"utility_test.cpp",
],
+ target: {
+ android: {
+ static_libs: [
+ "libcutils",
+ ],
+ },
+ },
}
diff --git a/Android.mk b/fs_mgr/liblp/Android.mk
similarity index 73%
rename from Android.mk
rename to fs_mgr/liblp/Android.mk
index 7c57258..7f7f891 100644
--- a/Android.mk
+++ b/fs_mgr/liblp/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-LOCAL_PATH := $(my-dir)
-include $(call first-makefiles-under,$(LOCAL_PATH))
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelLiblpTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
new file mode 100644
index 0000000..fe1002c
--- /dev/null
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelLiblpTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsKernelLiblpTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="test-timeout" value="1m"/>
+ <option name="precondition-first-api-level" value="29" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 3cd33b1..8797ea9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
@@ -29,6 +30,11 @@
namespace android {
namespace fs_mgr {
+std::optional<bool> MetadataBuilder::sABOverride;
+std::optional<bool> MetadataBuilder::sRetrofitDap;
+
+static const std::string kDefaultGroup = "default";
+
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@@ -113,18 +119,7 @@
if (!metadata) {
return nullptr;
}
- std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
- if (!builder) {
- return nullptr;
- }
- for (size_t i = 0; i < builder->block_devices_.size(); i++) {
- std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
- BlockDeviceInfo device_info;
- if (opener.GetInfo(partition_name, &device_info)) {
- builder->UpdateBlockDeviceInfo(i, device_info);
- }
- }
- return builder;
+ return New(*metadata.get(), &opener);
}
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
@@ -142,15 +137,88 @@
return builder;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
+ const IPartitionOpener* opener) {
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
if (!builder->Init(metadata)) {
return nullptr;
}
+ if (opener) {
+ for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+ std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+ BlockDeviceInfo device_info;
+ if (opener->GetInfo(partition_name, &device_info)) {
+ builder->UpdateBlockDeviceInfo(i, device_info);
+ }
+ }
+ }
return builder;
}
-MetadataBuilder::MetadataBuilder() {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
+ const std::string& source_partition,
+ uint32_t source_slot_number,
+ uint32_t target_slot_number) {
+ auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
+ if (!metadata) {
+ return nullptr;
+ }
+
+ // On non-retrofit devices there is only one location for metadata: the
+ // super partition. update_engine will remove and resize partitions as
+ // needed. On the other hand, for retrofit devices, we'll need to
+ // translate block device and group names to update their slot suffixes.
+ auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
+ if (GetBlockDevicePartitionName(*super_device) == "super" ||
+ !IsRetrofitDynamicPartitionsDevice()) {
+ return New(*metadata.get(), &opener);
+ }
+
+ // Clear partitions and extents, since they have no meaning on the target
+ // slot. We also clear groups since they are re-added during OTA.
+ metadata->partitions.clear();
+ metadata->extents.clear();
+ metadata->groups.clear();
+
+ std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+ std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+ // Translate block devices.
+ auto source_block_devices = std::move(metadata->block_devices);
+ for (const auto& source_block_device : source_block_devices) {
+ std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+ std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+ if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
+ // This should never happen. It means that the source metadata
+ // refers to a target or unknown block device.
+ LERROR << "Invalid block device for slot " << source_slot_suffix << ": "
+ << partition_name;
+ return nullptr;
+ }
+ std::string new_name =
+ partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+ target_slot_suffix;
+
+ auto new_device = source_block_device;
+ if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
+ LERROR << "Partition name too long: " << new_name;
+ return nullptr;
+ }
+ metadata->block_devices.emplace_back(new_device);
+ }
+
+ return New(*metadata.get(), &opener);
+}
+
+void MetadataBuilder::OverrideABForTesting(bool ab_device) {
+ sABOverride = ab_device;
+}
+
+void MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(bool retrofit) {
+ sRetrofitDap = retrofit;
+}
+
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
geometry_.struct_size = sizeof(geometry_);
@@ -184,23 +252,32 @@
if (!builder) {
return false;
}
-
- for (size_t i = 0; i < partition.num_extents; i++) {
- const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
- if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
- auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
- extent.target_data);
- builder->AddExtent(std::move(copy));
- } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
- auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
- builder->AddExtent(std::move(copy));
- }
- }
+ ImportExtents(builder, metadata, partition);
}
return true;
}
+void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
+ const LpMetadataPartition& source) {
+ for (size_t i = 0; i < source.num_extents; i++) {
+ const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
+ if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+ auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+ extent.target_data);
+ dest->AddExtent(std::move(copy));
+ } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+ auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+ dest->AddExtent(std::move(copy));
+ }
+ }
+}
+
static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+ if (device_info.logical_block_size == 0) {
+ LERROR << "Block device " << device_info.partition_name
+ << " logical block size must not be zero.";
+ return false;
+ }
if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
LERROR << "Block device " << device_info.partition_name
<< " logical block size must be a multiple of 512.";
@@ -267,7 +344,7 @@
out.alignment = device_info.alignment;
out.alignment_offset = device_info.alignment_offset;
out.size = device_info.size;
- if (device_info.partition_name.size() >= sizeof(out.partition_name)) {
+ if (device_info.partition_name.size() > sizeof(out.partition_name)) {
LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
return false;
}
@@ -337,7 +414,7 @@
geometry_.metadata_slot_count = metadata_slot_count;
geometry_.logical_block_size = logical_block_size;
- if (!AddGroup("default", 0)) {
+ if (!AddGroup(kDefaultGroup, 0)) {
return false;
}
return true;
@@ -353,7 +430,7 @@
}
Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
- return AddPartition(name, "default", attributes);
+ return AddPartition(name, kDefaultGroup, attributes);
}
Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
@@ -471,24 +548,33 @@
return free_regions;
}
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
+ uint64_t new_size, bool force_check) {
PartitionGroup* group = FindGroup(partition->group_name());
CHECK(group);
+ if (!force_check && new_size <= old_size) {
+ return true;
+ }
+
// Figure out how much we need to allocate, and whether our group has
// enough space remaining.
- uint64_t space_needed = aligned_size - partition->size();
+ uint64_t space_needed = new_size - old_size;
if (group->maximum_size() > 0) {
uint64_t group_size = TotalSizeOfGroup(group);
if (group_size >= group->maximum_size() ||
group->maximum_size() - group_size < space_needed) {
LERROR << "Partition " << partition->name() << " is part of group " << group->name()
- << " which does not have enough space free (" << space_needed << "requested, "
- << group_size << " used out of " << group->maximum_size();
+ << " which does not have enough space free (" << space_needed << " requested, "
+ << group_size << " used out of " << group->maximum_size() << ")";
return false;
}
}
+ return true;
+}
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
+ uint64_t space_needed = aligned_size - partition->size();
uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
@@ -498,11 +584,32 @@
CHECK_NE(sectors_per_block, 0);
CHECK(sectors_needed % sectors_per_block == 0);
- // Find gaps that we can use for new extents. Note we store new extents in a
- // temporary vector, and only commit them if we are guaranteed enough free
- // space.
+ if (IsABDevice() && !IsRetrofitMetadata() &&
+ GetPartitionSlotSuffix(partition->name()) == "_b") {
+ // Allocate "a" partitions top-down and "b" partitions bottom-up, to
+ // minimize fragmentation during OTA.
+ free_regions = PrioritizeSecondHalfOfSuper(free_regions);
+ }
+
+ // Note we store new extents in a temporary vector, and only commit them
+ // if we are guaranteed enough free space.
std::vector<std::unique_ptr<LinearExtent>> new_extents;
+
+ // If the last extent in the partition has a size < alignment, then the
+ // difference is unallocatable due to being misaligned. We peek for that
+ // case here to avoid wasting space.
+ if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+ sectors_needed -= extent->num_sectors();
+ new_extents.emplace_back(std::move(extent));
+ }
+
for (auto& region : free_regions) {
+ // Note: this comes first, since we may enter the loop not needing any
+ // more sectors.
+ if (!sectors_needed) {
+ break;
+ }
+
if (region.length() % sectors_per_block != 0) {
// This should never happen, because it would imply that we
// once allocated an extent that was not a multiple of the
@@ -525,9 +632,6 @@
auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
new_extents.push_back(std::move(extent));
sectors_needed -= sectors;
- if (!sectors_needed) {
- break;
- }
}
if (sectors_needed) {
LERROR << "Not enough free space to expand partition: " << partition->name();
@@ -541,17 +645,118 @@
return true;
}
+std::vector<MetadataBuilder::Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+ const std::vector<Interval>& free_list) {
+ const auto& super = block_devices_[0];
+ uint64_t first_sector = super.first_logical_sector;
+ uint64_t last_sector = super.size / LP_SECTOR_SIZE;
+ uint64_t midpoint = first_sector + (last_sector - first_sector) / 2;
+
+ // Choose an aligned sector for the midpoint. This could lead to one half
+ // being slightly larger than the other, but this will not restrict the
+ // size of partitions (it might lead to one extra extent if "B" overflows).
+ midpoint = AlignSector(super, midpoint);
+
+ std::vector<Interval> first_half;
+ std::vector<Interval> second_half;
+ for (const auto& region : free_list) {
+ // Note: deprioritze if not the main super partition. Even though we
+ // don't call this for retrofit devices, we will allow adding additional
+ // block devices on non-retrofit devices.
+ if (region.device_index != 0 || region.end <= midpoint) {
+ first_half.emplace_back(region);
+ continue;
+ }
+ if (region.start < midpoint && region.end > midpoint) {
+ // Split this into two regions.
+ first_half.emplace_back(region.device_index, region.start, midpoint);
+ second_half.emplace_back(region.device_index, midpoint, region.end);
+ } else {
+ second_half.emplace_back(region);
+ }
+ }
+ second_half.insert(second_half.end(), first_half.begin(), first_half.end());
+ return second_half;
+}
+
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+ Partition* partition, const std::vector<Interval>& free_list,
+ uint64_t sectors_needed) const {
+ if (partition->extents().empty()) {
+ return nullptr;
+ }
+ LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+ if (!extent) {
+ return nullptr;
+ }
+
+ // If the sector ends where the next aligned chunk begins, then there's
+ // no missing gap to try and allocate.
+ const auto& block_device = block_devices_[extent->device_index()];
+ uint64_t next_aligned_sector = AlignSector(block_device, extent->end_sector());
+ if (extent->end_sector() == next_aligned_sector) {
+ return nullptr;
+ }
+
+ uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+ auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+ extent->end_sector());
+ if (IsAnyRegionAllocated(*new_extent.get()) ||
+ IsAnyRegionCovered(free_list, *new_extent.get())) {
+ LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+ << new_extent->end_sector() << " was allocated or marked allocatable.";
+ return nullptr;
+ }
+ return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+ const LinearExtent& candidate) const {
+ for (const auto& region : regions) {
+ if (region.device_index == candidate.device_index() &&
+ (candidate.OwnsSector(region.start) || candidate.OwnsSector(region.end))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+ for (const auto& partition : partitions_) {
+ for (const auto& extent : partition->extents()) {
+ LinearExtent* linear = extent->AsLinearExtent();
+ if (!linear || linear->device_index() != candidate.device_index()) {
+ continue;
+ }
+ if (linear->OwnsSector(candidate.physical_sector()) ||
+ linear->OwnsSector(candidate.end_sector() - 1)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
partition->ShrinkTo(aligned_size);
}
std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+ if (!ValidatePartitionGroups()) {
+ return nullptr;
+ }
+
std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
metadata->header = header_;
metadata->geometry = geometry_;
// Assign this early so the extent table can read it.
- metadata->block_devices = block_devices_;
+ for (const auto& block_device : block_devices_) {
+ metadata->block_devices.emplace_back(block_device);
+ if (auto_slot_suffixing_) {
+ metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+ }
+ }
std::map<std::string, size_t> group_indices;
for (const auto& group : groups_) {
@@ -561,6 +766,9 @@
LERROR << "Partition group name is too long: " << group->name();
return nullptr;
}
+ if (auto_slot_suffixing_ && group->name() != kDefaultGroup) {
+ out.flags |= LP_GROUP_SLOT_SUFFIXED;
+ }
strncpy(out.name, group->name().c_str(), sizeof(out.name));
out.maximum_size = group->maximum_size();
@@ -587,6 +795,9 @@
part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
part.num_extents = static_cast<uint32_t>(partition->extents().size());
part.attributes = partition->attributes();
+ if (auto_slot_suffixing_) {
+ part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED;
+ }
auto iter = group_indices.find(partition->group_name());
if (iter == group_indices.end()) {
@@ -648,6 +859,11 @@
return false;
}
+bool MetadataBuilder::HasBlockDevice(const std::string& partition_name) const {
+ uint32_t index;
+ return FindBlockDeviceByName(partition_name, &index);
+}
+
bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,
BlockDeviceInfo* info) const {
uint32_t index;
@@ -682,9 +898,10 @@
<< block_device.size << ")";
return false;
}
- if (device_info.logical_block_size != geometry_.logical_block_size) {
- LERROR << "Device logical block size does not match (got " << device_info.logical_block_size
- << ", expected " << geometry_.logical_block_size << ")";
+ if (geometry_.logical_block_size % device_info.logical_block_size) {
+ LERROR << "Device logical block size is misaligned (block size="
+ << device_info.logical_block_size << ", alignment=" << geometry_.logical_block_size
+ << ")";
return false;
}
@@ -704,6 +921,10 @@
uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
uint64_t old_size = partition->size();
+ if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
+ return false;
+ }
+
if (aligned_size > old_size) {
if (!GrowPartition(partition, aligned_size)) {
return false;
@@ -728,7 +949,7 @@
}
void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
- if (group_name == "default") {
+ if (group_name == kDefaultGroup) {
// Cannot remove the default group.
return;
}
@@ -750,5 +971,162 @@
}
}
+static bool CompareBlockDevices(const LpMetadataBlockDevice& first,
+ const LpMetadataBlockDevice& second) {
+ // Note: we don't compare alignment, since it's a performance thing and
+ // won't affect whether old extents continue to work.
+ return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
+ GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+}
+
+bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
+ const std::set<std::string>& partition_names) {
+ // The block device list must be identical. We do not try to be clever and
+ // allow ordering changes or changes that don't affect partitions. This
+ // process is designed to allow the most common flashing scenarios and more
+ // complex ones should require a wipe.
+ if (metadata.block_devices.size() != block_devices_.size()) {
+ LINFO << "Block device tables does not match.";
+ return false;
+ }
+ for (size_t i = 0; i < metadata.block_devices.size(); i++) {
+ const LpMetadataBlockDevice& old_device = metadata.block_devices[i];
+ const LpMetadataBlockDevice& new_device = block_devices_[i];
+ if (!CompareBlockDevices(old_device, new_device)) {
+ LINFO << "Block device tables do not match";
+ return false;
+ }
+ }
+
+ // Import named partitions. Note that we do not attempt to merge group
+ // information here. If the device changed its group names, the old
+ // partitions will fail to merge. The same could happen if the group
+ // allocation sizes change.
+ for (const auto& partition : metadata.partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ if (partition_names.find(partition_name) == partition_names.end()) {
+ continue;
+ }
+ if (!ImportPartition(metadata, partition)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::ImportPartition(const LpMetadata& metadata,
+ const LpMetadataPartition& source) {
+ std::string partition_name = GetPartitionName(source);
+ Partition* partition = FindPartition(partition_name);
+ if (!partition) {
+ std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);
+ partition = AddPartition(partition_name, group_name, source.attributes);
+ if (!partition) {
+ return false;
+ }
+ }
+ if (partition->size() > 0) {
+ LINFO << "Importing partition table would overwrite non-empty partition: "
+ << partition_name;
+ return false;
+ }
+
+ ImportExtents(partition, metadata, source);
+
+ // Note: we've already increased the partition size by calling
+ // ImportExtents(). In order to figure out the size before that,
+ // we would have to iterate the extents and add up the linear
+ // segments. Instead, we just force ValidatePartitionSizeChange
+ // to check if the current configuration is acceptable.
+ if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {
+ partition->RemoveExtents();
+ return false;
+ }
+ return true;
+}
+
+void MetadataBuilder::SetAutoSlotSuffixing() {
+ auto_slot_suffixing_ = true;
+}
+
+bool MetadataBuilder::IsABDevice() {
+ if (sABOverride.has_value()) {
+ return *sABOverride;
+ }
+ return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+}
+
+bool MetadataBuilder::IsRetrofitDynamicPartitionsDevice() {
+ if (sRetrofitDap.has_value()) {
+ return *sRetrofitDap;
+ }
+ return android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false);
+}
+
+bool MetadataBuilder::IsRetrofitMetadata() const {
+ return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+}
+
+bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
+ uint64_t num_sectors, uint64_t physical_sector) {
+ uint32_t device_index;
+ if (!FindBlockDeviceByName(block_device, &device_index)) {
+ LERROR << "Could not find backing block device for extent: " << block_device;
+ return false;
+ }
+
+ auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);
+ partition->AddExtent(std::move(extent));
+ return true;
+}
+
+std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(const std::string& group_name) {
+ std::vector<Partition*> partitions;
+ for (const auto& partition : partitions_) {
+ if (partition->group_name() == group_name) {
+ partitions.emplace_back(partition.get());
+ }
+ }
+ return partitions;
+}
+
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+ if (!FindGroup(group_name)) {
+ LERROR << "Partition cannot change to unknown group: " << group_name;
+ return false;
+ }
+ partition->set_group_name(group_name);
+ return true;
+}
+
+bool MetadataBuilder::ValidatePartitionGroups() const {
+ for (const auto& group : groups_) {
+ if (!group->maximum_size()) {
+ continue;
+ }
+ uint64_t used = TotalSizeOfGroup(group.get());
+ if (used > group->maximum_size()) {
+ LERROR << "Partition group " << group->name() << " exceeds maximum size (" << used
+ << " bytes used, maximum " << group->maximum_size() << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MetadataBuilder::ChangeGroupSize(const std::string& group_name, uint64_t maximum_size) {
+ if (group_name == kDefaultGroup) {
+ LERROR << "Cannot change the size of the default group";
+ return false;
+ }
+ PartitionGroup* group = FindGroup(group_name);
+ if (!group) {
+ LERROR << "Cannot change size of unknown partition group: " << group_name;
+ return false;
+ }
+ group->set_maximum_size(maximum_size);
+ return true;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index c27e300..377ec68 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -25,7 +25,33 @@
using namespace android::fs_mgr;
using ::testing::ElementsAre;
-TEST(liblp, BuildBasic) {
+class Environment : public ::testing::Environment {
+ public:
+ void SetUp() override {
+ MetadataBuilder::OverrideABForTesting(false);
+ MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
+ }
+};
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(new Environment);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+class BuilderTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ MetadataBuilder::OverrideABForTesting(false);
+ MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
+ }
+ void TearDown() override {
+ MetadataBuilder::OverrideABForTesting(false);
+ MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
+ }
+};
+
+TEST_F(BuilderTest, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@@ -40,7 +66,7 @@
EXPECT_EQ(builder->FindPartition("system"), nullptr);
}
-TEST(liblp, ResizePartition) {
+TEST_F(BuilderTest, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@@ -88,13 +114,20 @@
EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);
EXPECT_EQ(extent->physical_sector(), 32);
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_EQ(FindPartition(*exported.get(), "not found"), nullptr);
+ auto entry = FindPartition(*exported.get(), "system");
+ ASSERT_NE(entry, nullptr);
+ ASSERT_EQ(GetPartitionSize(*exported.get(), *entry), 32768);
+
// Test shrinking to 0.
builder->ResizePartition(system, 0);
EXPECT_EQ(system->size(), 0);
EXPECT_EQ(system->extents().size(), 0);
}
-TEST(liblp, PartitionAlignment) {
+TEST_F(BuilderTest, PartitionAlignment) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@@ -110,7 +143,7 @@
EXPECT_EQ(system->extents().size(), 1);
}
-TEST(liblp, DiskAlignment) {
+TEST_F(BuilderTest, DiskAlignment) {
static const uint64_t kDiskSize = 1000000;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@@ -120,7 +153,7 @@
ASSERT_EQ(builder, nullptr);
}
-TEST(liblp, MetadataAlignment) {
+TEST_F(BuilderTest, MetadataAlignment) {
// Make sure metadata sizes get aligned up.
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
ASSERT_NE(builder, nullptr);
@@ -129,7 +162,7 @@
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
}
-TEST(liblp, InternalAlignment) {
+TEST_F(BuilderTest, InternalAlignment) {
// Test the metadata fitting within alignment.
BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
@@ -177,7 +210,7 @@
EXPECT_EQ(super_device->first_logical_sector, 160);
}
-TEST(liblp, InternalPartitionAlignment) {
+TEST_F(BuilderTest, InternalPartitionAlignment) {
BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
@@ -200,7 +233,7 @@
// Check that each starting sector is aligned.
for (const auto& extent : exported->extents) {
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
- EXPECT_EQ(extent.num_sectors, 8);
+ EXPECT_EQ(extent.num_sectors, 80);
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
@@ -208,10 +241,10 @@
}
// Sanity check one extent.
- EXPECT_EQ(exported->extents.back().target_data, 30656);
+ EXPECT_EQ(exported->extents.back().target_data, 3008);
}
-TEST(liblp, UseAllDiskSpace) {
+TEST_F(BuilderTest, UseAllDiskSpace) {
static constexpr uint64_t total = 1024 * 1024;
static constexpr uint64_t metadata = 1024;
static constexpr uint64_t slots = 2;
@@ -237,7 +270,7 @@
EXPECT_EQ(builder->AllocatableSpace(), allocatable);
}
-TEST(liblp, BuildComplex) {
+TEST_F(BuilderTest, BuildComplex) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -271,7 +304,7 @@
EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
}
-TEST(liblp, AddInvalidPartition) {
+TEST_F(BuilderTest, AddInvalidPartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -286,7 +319,7 @@
EXPECT_EQ(partition, nullptr);
}
-TEST(liblp, BuilderExport) {
+TEST_F(BuilderTest, BuilderExport) {
static const uint64_t kDiskSize = 1024 * 1024;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@@ -344,7 +377,7 @@
}
}
-TEST(liblp, BuilderImport) {
+TEST_F(BuilderTest, BuilderImport) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@@ -382,7 +415,7 @@
EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
}
-TEST(liblp, ExportNameTooLong) {
+TEST_F(BuilderTest, ExportNameTooLong) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
@@ -393,7 +426,7 @@
EXPECT_EQ(exported, nullptr);
}
-TEST(liblp, MetadataTooLarge) {
+TEST_F(BuilderTest, MetadataTooLarge) {
static const size_t kDiskSize = 128 * 1024;
static const size_t kMetadataSize = 64 * 1024;
@@ -423,11 +456,7 @@
EXPECT_EQ(builder, nullptr);
}
-TEST(liblp, block_device_info) {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- ASSERT_NE(fstab, nullptr);
-
+TEST_F(BuilderTest, block_device_info) {
PartitionOpener opener;
BlockDeviceInfo device_info;
@@ -444,7 +473,7 @@
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
}
-TEST(liblp, UpdateBlockDeviceInfo) {
+TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -477,18 +506,23 @@
EXPECT_EQ(new_info.size, 1024 * 1024);
new_info.logical_block_size = 512;
+ ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", new_info));
+ ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
+ EXPECT_EQ(new_info.logical_block_size, 4096);
+
+ new_info.logical_block_size = 7;
ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info));
ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info));
EXPECT_EQ(new_info.logical_block_size, 4096);
}
-TEST(liblp, InvalidBlockSize) {
+TEST_F(BuilderTest, InvalidBlockSize) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
EXPECT_EQ(builder, nullptr);
}
-TEST(liblp, AlignedExtentSize) {
+TEST_F(BuilderTest, AlignedExtentSize) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -499,14 +533,14 @@
EXPECT_EQ(partition->size(), 4096);
}
-TEST(liblp, AlignedFreeSpace) {
+TEST_F(BuilderTest, AlignedFreeSpace) {
// Only one sector free - at least one block is required.
BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
-TEST(liblp, HasDefaultGroup) {
+TEST_F(BuilderTest, HasDefaultGroup) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -514,7 +548,7 @@
EXPECT_FALSE(builder->AddGroup("default", 0));
}
-TEST(liblp, GroupSizeLimits) {
+TEST_F(BuilderTest, GroupSizeLimits) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -531,6 +565,58 @@
EXPECT_EQ(partition->size(), 16384);
}
+TEST_F(BuilderTest, ListPartitionsInGroup) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+ ASSERT_TRUE(builder->AddGroup("groupB", 16384));
+
+ Partition* system = builder->AddPartition("system", "groupA", 0);
+ Partition* vendor = builder->AddPartition("vendor", "groupA", 0);
+ Partition* product = builder->AddPartition("product", "groupB", 0);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ ASSERT_NE(product, nullptr);
+
+ auto groupA = builder->ListPartitionsInGroup("groupA");
+ auto groupB = builder->ListPartitionsInGroup("groupB");
+ auto groupC = builder->ListPartitionsInGroup("groupC");
+ ASSERT_THAT(groupA, ElementsAre(system, vendor));
+ ASSERT_THAT(groupB, ElementsAre(product));
+ ASSERT_TRUE(groupC.empty());
+}
+
+TEST_F(BuilderTest, ChangeGroups) {
+ BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("groupA", 16384));
+ ASSERT_TRUE(builder->AddGroup("groupB", 32768));
+
+ Partition* system = builder->AddPartition("system", "groupA", 0);
+ Partition* vendor = builder->AddPartition("vendor", "groupB", 0);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ ASSERT_NE(builder->Export(), nullptr);
+
+ ASSERT_FALSE(builder->ChangePartitionGroup(system, "groupXYZ"));
+ ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupB"));
+ ASSERT_NE(builder->Export(), nullptr);
+
+ // Violate group constraint by reassigning groups.
+ ASSERT_TRUE(builder->ResizePartition(system, 16384 + 4096));
+ ASSERT_TRUE(builder->ChangePartitionGroup(system, "groupA"));
+ ASSERT_EQ(builder->Export(), nullptr);
+
+ ASSERT_FALSE(builder->ChangeGroupSize("default", 2));
+ ASSERT_FALSE(builder->ChangeGroupSize("unknown", 2));
+ ASSERT_TRUE(builder->ChangeGroupSize("groupA", 32768));
+ ASSERT_NE(builder->Export(), nullptr);
+}
+
constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
return x << 30;
}
@@ -538,7 +624,7 @@
return x << 20;
}
-TEST(liblp, RemoveAndAddFirstPartition) {
+TEST_F(BuilderTest, RemoveAndAddFirstPartition) {
auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
ASSERT_NE(nullptr, builder);
ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
@@ -561,7 +647,7 @@
ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
}
-TEST(liblp, ListGroups) {
+TEST_F(BuilderTest, ListGroups) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -571,7 +657,7 @@
ASSERT_THAT(groups, ElementsAre("default", "example"));
}
-TEST(liblp, RemoveGroupAndPartitions) {
+TEST_F(BuilderTest, RemoveGroupAndPartitions) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@@ -588,7 +674,7 @@
ASSERT_NE(builder->FindPartition("system"), nullptr);
}
-TEST(liblp, MultipleBlockDevices) {
+TEST_F(BuilderTest, MultipleBlockDevices) {
std::vector<BlockDeviceInfo> partitions = {
BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
@@ -632,3 +718,196 @@
EXPECT_EQ(metadata->extents[2].target_data, 1472);
EXPECT_EQ(metadata->extents[2].target_source, 2);
}
+
+TEST_F(BuilderTest, ImportPartitionsOk) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {"vendor"}));
+ EXPECT_NE(builder->FindPartition("vendor"), nullptr);
+ EXPECT_EQ(builder->FindPartition("system"), nullptr);
+
+ unique_ptr<LpMetadata> new_metadata = builder->Export();
+ ASSERT_NE(new_metadata, nullptr);
+
+ ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));
+ ASSERT_EQ(GetPartitionName(exported->partitions[1]), "vendor");
+ ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));
+ ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), "vendor");
+
+ const LpMetadataExtent& extent_a =
+ exported->extents[exported->partitions[1].first_extent_index];
+ const LpMetadataExtent& extent_b =
+ new_metadata->extents[new_metadata->partitions[0].first_extent_index];
+ EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);
+ EXPECT_EQ(extent_a.target_type, extent_b.target_type);
+ EXPECT_EQ(extent_a.target_data, extent_b.target_data);
+ EXPECT_EQ(extent_a.target_source, extent_b.target_source);
+}
+
+TEST_F(BuilderTest, ImportPartitionsFail) {
+ unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+
+ Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
+ Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(system, nullptr);
+ ASSERT_NE(vendor, nullptr);
+ EXPECT_EQ(builder->ResizePartition(system, 65536), true);
+ EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
+ EXPECT_EQ(builder->ResizePartition(system, 98304), true);
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+
+ // Different device size.
+ builder = MetadataBuilder::New(1024 * 2048, 1024, 2);
+ ASSERT_NE(builder, nullptr);
+ EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
+}
+
+TEST_F(BuilderTest, ABExtents) {
+ BlockDeviceInfo device_info("super", 10_GiB, 768 * 1024, 0, 4096);
+
+ // A and B slots should be allocated from separate halves of the partition,
+ // to mitigate allocating too many extents. (b/120433288)
+ MetadataBuilder::OverrideABForTesting(true);
+ auto builder = MetadataBuilder::New(device_info, 65536, 2);
+ ASSERT_NE(builder, nullptr);
+ Partition* system_a = builder->AddPartition("system_a", 0);
+ ASSERT_NE(system_a, nullptr);
+ Partition* system_b = builder->AddPartition("system_b", 0);
+ ASSERT_NE(system_b, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_a, 2_GiB));
+ ASSERT_TRUE(builder->ResizePartition(system_b, 2_GiB));
+
+ builder->RemovePartition("system_a");
+ system_a = builder->AddPartition("system_a", 0);
+ ASSERT_NE(system_a, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system_a, 3_GiB));
+
+ EXPECT_EQ(system_a->extents().size(), static_cast<size_t>(1));
+ EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(1));
+ ASSERT_TRUE(builder->ResizePartition(system_b, 6_GiB));
+ EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(2));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_EQ(exported->extents.size(), static_cast<size_t>(3));
+ EXPECT_EQ(exported->extents[0].target_data, 10487808);
+ EXPECT_EQ(exported->extents[0].num_sectors, 10483712);
+ EXPECT_EQ(exported->extents[1].target_data, 6292992);
+ EXPECT_EQ(exported->extents[1].num_sectors, 2099200);
+ EXPECT_EQ(exported->extents[2].target_data, 1536);
+ EXPECT_EQ(exported->extents[2].num_sectors, 6291456);
+}
+
+TEST_F(BuilderTest, PartialExtents) {
+ // super has a minimum extent size of 768KiB.
+ BlockDeviceInfo device_info("super", 1_GiB, 768 * 1024, 0, 4096);
+ auto builder = MetadataBuilder::New(device_info, 65536, 1);
+ ASSERT_NE(builder, nullptr);
+ Partition* system = builder->AddPartition("system", 0);
+ ASSERT_NE(system, nullptr);
+ Partition* vendor = builder->AddPartition("vendor", 0);
+ ASSERT_NE(vendor, nullptr);
+ ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));
+ ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));
+ ASSERT_EQ(system->size(), device_info.alignment + 4096);
+ ASSERT_EQ(vendor->size(), device_info.alignment);
+
+ ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));
+ ASSERT_EQ(system->extents().size(), static_cast<size_t>(1));
+
+ unique_ptr<LpMetadata> exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_EQ(exported->extents.size(), static_cast<size_t>(2));
+ EXPECT_EQ(exported->extents[0].target_data, 1536);
+ EXPECT_EQ(exported->extents[0].num_sectors, 3072);
+ EXPECT_EQ(exported->extents[1].target_data, 4608);
+ EXPECT_EQ(exported->extents[1].num_sectors, 1536);
+}
+
+TEST_F(BuilderTest, UpdateSuper) {
+ // Build the on-disk metadata that we saw before flashing.
+ auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+
+ Partition* partition = builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 3608576));
+
+ partition = builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 5510144));
+
+ partition = builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 2048));
+
+ partition = builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1901568, 7955456));
+
+ partition = builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 1521664, 9857024));
+
+ partition = builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY);
+ ASSERT_NE(partition, nullptr);
+ ASSERT_TRUE(builder->AddLinearExtent(partition, "super", 3606528, 11378688));
+
+ auto on_disk = builder->Export();
+ ASSERT_NE(on_disk, nullptr);
+
+ // Build the super_empty from the new build.
+ builder = MetadataBuilder::New(8145338368ULL, 65536, 3);
+ ASSERT_NE(builder, nullptr);
+
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_a", 4068474880ULL));
+ ASSERT_TRUE(builder->AddGroup("google_dynamic_partitions_b", 4068474880ULL));
+ ASSERT_NE(builder->AddPartition("system_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("system_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("vendor_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_a", "google_dynamic_partitions_a",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+ ASSERT_NE(builder->AddPartition("product_b", "google_dynamic_partitions_b",
+ LP_PARTITION_ATTR_READONLY),
+ nullptr);
+
+ std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
+ ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 46bdfa4..58a88b5 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -27,6 +27,12 @@
namespace android {
namespace fs_mgr {
+using android::base::unique_fd;
+
+#if defined(_WIN32)
+static const int O_NOFOLLOW = 0;
+#endif
+
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
@@ -61,10 +67,10 @@
return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
}
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
- android::base::unique_fd fd(open(file, O_RDONLY));
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {
+ unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
- PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
+ PERROR << __PRETTY_FUNCTION__ << " open failed: " << image_file;
return nullptr;
}
return ReadFromImageFile(fd);
@@ -83,8 +89,8 @@
return true;
}
-bool WriteToImageFile(const char* file, const LpMetadata& input) {
- android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
return false;
@@ -92,12 +98,12 @@
return WriteToImageFile(fd, input);
}
-SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images)
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify)
: metadata_(metadata),
geometry_(metadata.geometry),
block_size_(block_size),
- file_(nullptr, sparse_file_destroy),
+ sparsify_(sparsify),
images_(images) {
uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
@@ -121,7 +127,7 @@
return;
}
- uint64_t num_blocks = total_size % block_size;
+ uint64_t num_blocks = total_size / block_size;
if (num_blocks >= UINT_MAX) {
// libsparse counts blocks in unsigned 32-bit integers, so we check to
// make sure we're not going to overflow.
@@ -129,20 +135,32 @@
return;
}
- file_.reset(sparse_file_new(block_size_, total_size));
- if (!file_) {
- LERROR << "Could not allocate sparse file of size " << total_size;
+ for (const auto& block_device : metadata.block_devices) {
+ SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);
+ if (!file) {
+ LERROR << "Could not allocate sparse file of size " << block_device.size;
+ return;
+ }
+ device_images_.emplace_back(std::move(file));
}
}
-bool SparseBuilder::Export(const char* file) {
- android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+bool ImageBuilder::IsValid() const {
+ return device_images_.size() == metadata_.block_devices.size();
+}
+
+bool ImageBuilder::Export(const std::string& file) {
+ unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
return false;
}
- // No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(file_.get(), fd, false, true, false);
+ if (device_images_.size() > 1) {
+ LERROR << "Cannot export to a single image on retrofit builds.";
+ return false;
+ }
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -150,13 +168,35 @@
return true;
}
-bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
+ for (size_t i = 0; i < device_images_.size(); i++) {
+ std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
+ std::string file_name = "super_" + name + ".img";
+ std::string file_path = output_dir + "/" + file_name;
+
+ static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW;
+ unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
+ if (fd < 0) {
+ PERROR << "open failed: " << file_path;
+ return false;
+ }
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
+ if (ret != 0) {
+ LERROR << "sparse_file_write failed (error code " << ret << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
uint32_t block;
if (!SectorToBlock(sector, &block)) {
return false;
}
void* data = const_cast<char*>(blob.data());
- int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+ int ret = sparse_file_add_data(file, data, blob.size(), block);
if (ret != 0) {
LERROR << "sparse_file_add_data failed (error code " << ret << ")";
return false;
@@ -164,7 +204,7 @@
return true;
}
-bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
// The caller must ensure that the metadata has an alignment that is a
// multiple of the block size. liblp will take care of the rest, ensuring
// that all partitions are on an aligned boundary. Therefore all writes
@@ -179,8 +219,12 @@
return true;
}
-bool SparseBuilder::Build() {
- if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
+ return (block * block_size_) / LP_SECTOR_SIZE;
+}
+
+bool ImageBuilder::Build() {
+ if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
LERROR << "Could not add initial sparse block for reserved zeroes";
return false;
}
@@ -194,7 +238,13 @@
for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
all_metadata_ += metadata_blob;
}
- if (!AddData(all_metadata_, 0)) {
+
+ uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;
+ if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {
+ return false;
+ }
+
+ if (!CheckExtentOrdering()) {
return false;
}
@@ -226,15 +276,12 @@
return true;
}
-bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
- const std::string& file) {
- if (partition.num_extents != 1) {
- LERROR << "Partition for new tables should not have more than one extent: "
- << GetPartitionName(partition);
- return false;
- }
+bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+ const std::string& file) {
+ // Track which extent we're processing.
+ uint32_t extent_index = partition.first_extent_index;
- const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+ const LpMetadataExtent& extent = metadata_.extents[extent_index];
if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
return false;
@@ -252,9 +299,11 @@
LERROR << "Could not compute image size";
return false;
}
- if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+ uint64_t partition_size = ComputePartitionSize(partition);
+ if (file_length > partition_size) {
LERROR << "Image for partition '" << GetPartitionName(partition)
- << "' is greater than its size";
+ << "' is greater than its size (" << file_length << ", expected " << partition_size
+ << ")";
return false;
}
if (SeekFile64(fd, 0, SEEK_SET)) {
@@ -262,14 +311,39 @@
return false;
}
+ // We track the current logical sector and the position the current extent
+ // ends at.
+ uint64_t output_sector = 0;
+ uint64_t extent_last_sector = extent.num_sectors;
+
+ // We also track the output device and the current output block within that
+ // device.
uint32_t output_block;
if (!SectorToBlock(extent.target_data, &output_block)) {
return false;
}
+ sparse_file* output_device = device_images_[extent.target_source].get();
+ // Proceed to read the file and build sparse images.
uint64_t pos = 0;
uint64_t remaining = file_length;
while (remaining) {
+ // Check if we need to advance to the next extent.
+ if (output_sector == extent_last_sector) {
+ extent_index++;
+ if (extent_index >= partition.first_extent_index + partition.num_extents) {
+ LERROR << "image is larger than extent table";
+ return false;
+ }
+
+ const LpMetadataExtent& extent = metadata_.extents[extent_index];
+ extent_last_sector += extent.num_sectors;
+ output_device = device_images_[extent.target_source].get();
+ if (!SectorToBlock(extent.target_data, &output_block)) {
+ return false;
+ }
+ }
+
uint32_t buffer[block_size_ / sizeof(uint32_t)];
size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
@@ -277,13 +351,13 @@
return false;
}
if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
- int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+ int rv = sparse_file_add_fd(output_device, fd, pos, read_size, output_block);
if (rv) {
LERROR << "sparse_file_add_fd failed with code: " << rv;
return false;
}
} else {
- int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+ int rv = sparse_file_add_fill(output_device, buffer[0], read_size, output_block);
if (rv) {
LERROR << "sparse_file_add_fill failed with code: " << rv;
return false;
@@ -291,54 +365,91 @@
}
pos += read_size;
remaining -= read_size;
+ output_sector += block_size_ / LP_SECTOR_SIZE;
output_block++;
}
return true;
}
-int SparseBuilder::OpenImageFile(const std::string& file) {
- android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+ uint64_t sectors = 0;
+ for (size_t i = 0; i < partition.num_extents; i++) {
+ sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
+ }
+ return sectors * LP_SECTOR_SIZE;
+}
+
+// For simplicity, we don't allow serializing any configuration: extents must
+// be ordered, such that any extent at position I in the table occurs *before*
+// any extent after position I, for the same block device. We validate that
+// here.
+//
+// Without this, it would be more difficult to find the appropriate extent for
+// an output block. With this guarantee it is a linear walk.
+bool ImageBuilder::CheckExtentOrdering() {
+ std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
+
+ for (const auto& extent : metadata_.extents) {
+ if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+ LERROR << "Extents must all be type linear.";
+ return false;
+ }
+ if (extent.target_data <= last_sectors[extent.target_source]) {
+ LERROR << "Extents must appear in increasing order.";
+ return false;
+ }
+ if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {
+ LERROR << "Extents must be aligned to the block size.";
+ return false;
+ }
+ last_sectors[extent.target_source] = extent.target_data;
+ }
+ return true;
+}
+
+int ImageBuilder::OpenImageFile(const std::string& file) {
+ android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
return -1;
}
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
- sparse_file_import(source_fd, true, true), sparse_file_destroy);
+ SparsePtr source(sparse_file_import(source_fd, true, true), sparse_file_destroy);
if (!source) {
int fd = source_fd.get();
temp_fds_.push_back(std::move(source_fd));
return fd;
}
- char temp_file[PATH_MAX];
- snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir);
- android::base::unique_fd temp_fd(mkstemp(temp_file));
- if (temp_fd < 0) {
- PERROR << "mkstemp failed";
- return -1;
- }
- if (unlink(temp_file) < 0) {
- PERROR << "unlink failed";
+ TemporaryFile tf;
+ if (tf.fd < 0) {
+ PERROR << "make temporary file failed";
return -1;
}
// We temporarily unsparse the file, rather than try to merge its chunks.
- int rv = sparse_file_write(source.get(), temp_fd, false, false, false);
+ int rv = sparse_file_write(source.get(), tf.fd, false, false, false);
if (rv) {
LERROR << "sparse_file_write failed with code: " << rv;
return -1;
}
- temp_fds_.push_back(std::move(temp_fd));
+ temp_fds_.push_back(android::base::unique_fd(tf.release()));
return temp_fds_.back().get();
}
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images) {
- SparseBuilder builder(metadata, block_size, images);
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.Export(file);
}
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
+ return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a9ef8ce..a284d2e 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -32,30 +32,37 @@
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
bool WriteToImageFile(int fd, const LpMetadata& metadata);
-// We use an object to build the sparse file since it requires that data
+// We use an object to build the image file since it requires that data
// pointers be held alive until the sparse file is destroyed. It's easier
// to do this when the data pointers are all in one place.
-class SparseBuilder {
+class ImageBuilder {
public:
- SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
+ ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
bool Build();
- bool Export(const char* file);
- bool IsValid() const { return file_ != nullptr; }
+ bool Export(const std::string& file);
+ bool ExportFiles(const std::string& dir);
+ bool IsValid() const;
- sparse_file* file() const { return file_.get(); }
+ using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
+ const std::vector<SparsePtr>& device_images() const { return device_images_; }
private:
- bool AddData(const std::string& blob, uint64_t sector);
+ bool AddData(sparse_file* file, const std::string& blob, uint64_t sector);
bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
int OpenImageFile(const std::string& file);
bool SectorToBlock(uint64_t sector, uint32_t* block);
+ uint64_t BlockToSector(uint64_t block) const;
+ bool CheckExtentOrdering();
+ uint64_t ComputePartitionSize(const LpMetadataPartition& partition) const;
const LpMetadata& metadata_;
const LpMetadataGeometry& geometry_;
uint32_t block_size_;
- std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+ bool sparsify_;
+
+ std::vector<SparsePtr> device_images_;
std::string all_metadata_;
std::map<std::string, std::string> images_;
std::vector<android::base::unique_fd> temp_fds_;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index f9de106..a2221ef 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -22,6 +22,8 @@
#include <map>
#include <memory>
+#include <optional>
+#include <set>
#include "liblp.h"
#include "partition_opener.h"
@@ -64,6 +66,10 @@
uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
uint32_t device_index() const { return device_index_; }
+ bool OwnsSector(uint64_t sector) const {
+ return sector >= physical_sector_ && sector < end_sector();
+ }
+
private:
uint32_t device_index_;
uint64_t physical_sector_;
@@ -78,6 +84,8 @@
};
class PartitionGroup final {
+ friend class MetadataBuilder;
+
public:
explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
: name_(name), maximum_size_(maximum_size) {}
@@ -86,6 +94,8 @@
uint64_t maximum_size() const { return maximum_size_; }
private:
+ void set_maximum_size(uint64_t maximum_size) { maximum_size_ = maximum_size; }
+
std::string name_;
uint64_t maximum_size_;
};
@@ -114,6 +124,7 @@
private:
void ShrinkTo(uint64_t aligned_size);
+ void set_group_name(const std::string& group_name) { group_name_ = group_name; }
std::string name_;
std::string group_name_;
@@ -149,10 +160,24 @@
static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
uint32_t slot_number);
+ // This is when performing an A/B update. The source partition must be a
+ // super partition. On a normal device, the metadata for the source slot
+ // is imported and the target slot is ignored. On a retrofit device, the
+ // metadata may not have the target slot's devices listed yet, in which
+ // case, it is automatically upgraded to include all available block
+ // devices.
+ static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
+ const std::string& source_partition,
+ uint32_t source_slot_number,
+ uint32_t target_slot_number);
+
// Import an existing table for modification. If the table is not valid, for
// example it contains duplicate partition names, then nullptr is returned.
- // This method is for testing or changing off-line tables.
- static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+ //
+ // If an IPartitionOpener is specified, then block device informatiom will
+ // be updated.
+ static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,
+ const IPartitionOpener* opener = nullptr);
// Helper function for a single super partition, for tests.
static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
@@ -171,6 +196,12 @@
return New(device_info, metadata_max_size, metadata_slot_count);
}
+ // Used by the test harness to override whether the device is "A/B".
+ static void OverrideABForTesting(bool ab_device);
+
+ // Used by the test harness to override whether the device is "retrofitting dynamic partitions".
+ static void OverrideRetrofitDynamicParititonsForTesting(bool retrofit);
+
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@@ -200,6 +231,10 @@
// Find a group by name. If no group is found, nullptr is returned.
PartitionGroup* FindGroup(const std::string& name);
+ // Add a predetermined extent to a partition.
+ bool AddLinearExtent(Partition* partition, const std::string& block_device,
+ uint64_t num_sectors, uint64_t physical_sector);
+
// Grow or shrink a partition to the requested size. This size will be
// rounded UP to the nearest block (512 bytes).
//
@@ -212,6 +247,21 @@
// underlying filesystem or contents of the partition on disk.
bool ResizePartition(Partition* partition, uint64_t requested_size);
+ // Return the list of partitions belonging to a group.
+ std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
+
+ // Changes a partition's group. Size constraints will not be checked until
+ // the metadata is exported, to avoid errors during potential group and
+ // size shuffling operations. This will return false if the new group does
+ // not exist.
+ bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+
+ // Changes the size of a partition group. Size constraints will not be
+ // checked until metadata is exported, to avoid errors during group
+ // reshuffling. This will return false if the group does not exist, or if
+ // the group name is "default".
+ bool ChangeGroupSize(const std::string& group_name, uint64_t maximum_size);
+
// Amount of space that can be allocated to logical partitions.
uint64_t AllocatableSpace() const;
uint64_t UsedSpace() const;
@@ -222,9 +272,23 @@
// Remove all partitions belonging to a group, then remove the group.
void RemoveGroupAndPartitions(const std::string& group_name);
+ // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
+ void SetAutoSlotSuffixing();
+
+ // If set, checks for slot suffixes will be ignored internally.
+ void IgnoreSlotSuffixing();
+
bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
+ // Attempt to preserve the named partitions from an older metadata. If this
+ // is not possible (for example, the block device list has changed) then
+ // false is returned.
+ bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);
+
+ // Return true if a block device is found, else false.
+ bool HasBlockDevice(const std::string& partition_name) const;
+
private:
MetadataBuilder();
MetadataBuilder(const MetadataBuilder&) = delete;
@@ -240,6 +304,22 @@
uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
+ bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,
+ bool force_check);
+ void ImportExtents(Partition* dest, const LpMetadata& metadata,
+ const LpMetadataPartition& source);
+ bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
+
+ // Return true if the device is an AB device.
+ static bool IsABDevice();
+
+ // Return true if the device is retrofitting dynamic partitions.
+ static bool IsRetrofitDynamicPartitionsDevice();
+
+ // Return true if "this" metadata represents a metadata on a retrofit device.
+ bool IsRetrofitMetadata() const;
+
+ bool ValidatePartitionGroups() const;
struct Interval {
uint32_t device_index;
@@ -257,14 +337,25 @@
}
};
std::vector<Interval> GetFreeRegions() const;
+ bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+ const LinearExtent& candidate) const;
+ bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
void ExtentsToFreeList(const std::vector<Interval>& extents,
std::vector<Interval>* free_regions) const;
+ std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+ std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+ const std::vector<Interval>& free_list,
+ uint64_t sectors_needed) const;
+
+ static std::optional<bool> sABOverride;
+ static std::optional<bool> sRetrofitDap;
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;
std::vector<std::unique_ptr<PartitionGroup>> groups_;
std::vector<LpMetadataBlockDevice> block_devices_;
+ bool auto_slot_suffixing_;
};
// Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 4669cea..135a1b3 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,12 +72,21 @@
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
+bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
+std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
+// Similar to WriteToSparseFile, this will generate an image that can be
+// flashed to a device directly. However unlike WriteToSparseFile, it
+// is intended for retrofit devices, and will generate one sparse file per
+// block device (each named super_<name>.img) and placed in the specified
+// output folder.
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify);
+
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
@@ -90,8 +99,17 @@
// Return the total size of all partitions comprising the super partition.
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
-// Helper to return a slot number for a slot suffix.
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
+// Slot suffix helpers.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+std::string SlotSuffixForSlotNumber(uint32_t slot_number);
+std::string GetPartitionSlotSuffix(const std::string& partition_name);
+
+// Helpers for common functions.
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 1e40df3..8934aaf 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 8
+#define LP_METADATA_MAJOR_VERSION 10
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@@ -47,10 +47,19 @@
* device mapper, the block device will be created as read-only.
*/
#define LP_PARTITION_ATTR_NONE 0x0
-#define LP_PARTITION_ATTR_READONLY 0x1
+#define LP_PARTITION_ATTR_READONLY (1 << 0)
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the partition name needs a slot suffix applied. The slot suffix is
+ * determined by the metadata slot number (0 = _a, 1 = _b).
+ */
+#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
/* Mask that defines all valid attributes. */
-#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY)
+#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
/* Default name of the physical partition that holds logical partition entries.
* The layout of this partition will look like:
@@ -118,7 +127,7 @@
* num_entries, and the result must not overflow a 32-bit signed integer.
*/
typedef struct LpMetadataTableDescriptor {
- /* 0: Location of the table, relative to the metadata header. */
+ /* 0: Location of the table, relative to end of the metadata header. */
uint32_t offset;
/* 4: Number of entries in the table. */
uint32_t num_entries;
@@ -258,9 +267,18 @@
/* 0: Name of this group. Any unused characters must be 0. */
char name[36];
- /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
+ /* 36: Flags (see LP_GROUP_*). */
+ uint32_t flags;
+
+ /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
uint64_t maximum_size;
-} LpMetadataPartitionGroup;
+} __attribute__((packed)) LpMetadataPartitionGroup;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. If set, the group needs a slot suffix to be interpreted
+ * correctly. The suffix is automatically applied by ReadMetadata().
+ */
+#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
/* This struct defines an entry in the block_devices table. There must be at
* least one device, and the first device must represent the partition holding
@@ -302,7 +320,20 @@
/* 24: Partition name in the GPT. Any unused characters must be 0. */
char partition_name[36];
-} LpMetadataBlockDevice;
+
+ /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
+ uint32_t flags;
+} __attribute__((packed)) LpMetadataBlockDevice;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the block device needs a slot suffix applied before being used with
+ * IPartitionOpener. The slot suffix is determined by the metadata slot number
+ * (0 = _a, 1 = _b).
+ */
+#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
#ifdef __cplusplus
} /* extern "C" */
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 603e5c0..70dd85f 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,6 +21,8 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
@@ -38,6 +40,7 @@
static const size_t kDiskSize = 131072;
static const size_t kMetadataSize = 512;
static const size_t kMetadataSlots = 2;
+static const BlockDeviceInfo kSuperInfo{"super", kDiskSize, 0, 0, 4096};
// Helper function for creating an in-memory file descriptor. This lets us
// simulate read/writing logical partition metadata as if we had a block device
@@ -79,6 +82,12 @@
return builder;
}
+class DefaultPartitionOpener final : public TestPartitionOpener {
+ public:
+ explicit DefaultPartitionOpener(int fd)
+ : TestPartitionOpener({{"super", fd}}, {{"super", kSuperInfo}}) {}
+};
+
static bool AddDefaultPartitions(MetadataBuilder* builder) {
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
if (!system) {
@@ -103,7 +112,7 @@
return {};
}
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
if (!FlashPartitionTable(opener, "super", *exported.get())) {
return {};
}
@@ -119,7 +128,7 @@
ASSERT_TRUE(GetDescriptorSize(fd, &size));
ASSERT_EQ(size, kDiskSize);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Verify that we can't read unwritten metadata.
ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
@@ -138,7 +147,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
}
@@ -152,7 +161,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Export and flash.
unique_ptr<LpMetadata> exported = builder->Export();
@@ -198,7 +207,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
@@ -243,7 +252,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Make sure all slots are filled.
unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
@@ -262,7 +271,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
@@ -291,7 +300,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
LpMetadataGeometry geometry;
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
@@ -310,7 +319,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
char corruption[LP_METADATA_GEOMETRY_SIZE];
memset(corruption, 0xff, sizeof(corruption));
@@ -330,7 +339,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
@@ -378,7 +387,7 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
// Check that we are able to write our table.
ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
@@ -487,7 +496,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -515,7 +524,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -544,7 +553,7 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- TestPartitionOpener opener({{"super", fd}});
+ DefaultPartitionOpener opener(fd);
BadWriter writer;
@@ -591,14 +600,16 @@
ASSERT_NE(exported, nullptr);
// Build the sparse file.
- SparseBuilder sparse(*exported.get(), 512, {});
+ ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
ASSERT_TRUE(sparse.IsValid());
- sparse_file_verbose(sparse.file());
ASSERT_TRUE(sparse.Build());
+ const auto& images = sparse.device_images();
+ ASSERT_EQ(images.size(), static_cast<size_t>(1));
+
// Write it to the fake disk.
ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
- int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+ int ret = sparse_file_write(images[0].get(), fd.get(), false, false, false);
ASSERT_EQ(ret, 0);
// Verify that we can read both sets of metadata.
@@ -608,3 +619,108 @@
ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
}
+
+TEST(liblp, AutoSlotSuffixing) {
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+ builder->SetAutoSlotSuffixing();
+
+ auto fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Note: we bind the same fd to both names, since we want to make sure the
+ // exact same bits are getting read back in each test.
+ TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+ {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+ auto metadata = ReadMetadata(opener, "super_b", 1);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_b");
+ ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_b");
+ ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_b");
+ EXPECT_EQ(metadata->groups[0].flags, 0);
+ EXPECT_EQ(metadata->groups[1].flags, 0);
+
+ metadata = ReadMetadata(opener, "super_a", 0);
+ ASSERT_NE(metadata, nullptr);
+ ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_a");
+ ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a");
+ ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), "default");
+ EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), "example_a");
+ EXPECT_EQ(metadata->groups[0].flags, 0);
+ EXPECT_EQ(metadata->groups[1].flags, 0);
+}
+
+TEST(liblp, UpdateRetrofit) {
+ MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(true);
+
+ unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+ ASSERT_NE(builder, nullptr);
+ ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+ ASSERT_TRUE(builder->AddGroup("example", 0));
+ builder->SetAutoSlotSuffixing();
+
+ auto fd = CreateFakeDisk();
+ ASSERT_GE(fd, 0);
+
+ // Note: we bind the same fd to both names, since we want to make sure the
+ // exact same bits are getting read back in each test.
+ TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+ {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+ auto exported = builder->Export();
+ ASSERT_NE(exported, nullptr);
+ ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+ builder = MetadataBuilder::NewForUpdate(opener, "super_a", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super_b");
+ ASSERT_TRUE(updated->groups.empty());
+ ASSERT_TRUE(updated->partitions.empty());
+ ASSERT_TRUE(updated->extents.empty());
+}
+
+TEST(liblp, UpdateNonRetrofit) {
+ MetadataBuilder::OverrideRetrofitDynamicParititonsForTesting(false);
+
+ unique_fd fd = CreateFlashedDisk();
+ ASSERT_GE(fd, 0);
+
+ DefaultPartitionOpener opener(fd);
+ auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ ASSERT_NE(builder, nullptr);
+ auto updated = builder->Export();
+ ASSERT_NE(updated, nullptr);
+ ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+ EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
+}
+
+TEST(liblp, ReadSuperPartition) {
+ auto slot_suffix = fs_mgr_get_slot_suffix();
+ auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto super_name = fs_mgr_get_super_partition_name(slot_number);
+ auto metadata = ReadMetadata(super_name, slot_number);
+ ASSERT_NE(metadata, nullptr);
+
+ if (!slot_suffix.empty()) {
+ auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+ auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+ auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+ auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+ ASSERT_NE(other_metadata, nullptr);
+ }
+}
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 77b0e62..cc4a882 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -19,12 +19,14 @@
#if defined(__linux__)
#include <linux/fs.h>
#endif
+#if !defined(_WIN32)
#include <sys/ioctl.h>
-#include <sys/stat.h>
+#endif
#include <sys/types.h>
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include "utility.h"
@@ -36,7 +38,7 @@
namespace {
std::string GetPartitionAbsolutePath(const std::string& path) {
- if (path[0] == '/') {
+ if (android::base::StartsWith(path, "/")) {
return path;
}
return "/dev/block/by-name/" + path;
@@ -44,7 +46,7 @@
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
#if defined(__linux__)
- unique_fd fd(open(block_device.c_str(), O_RDONLY));
+ unique_fd fd = GetControlFileOrOpen(block_device.c_str(), O_RDONLY);
if (fd < 0) {
PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed";
return false;
@@ -53,18 +55,24 @@
return false;
}
if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed on " << block_device;
return false;
}
int alignment_offset;
if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKALIGNOFF failed on " << block_device;
return false;
}
+ // The kernel can return -1 here when misaligned devices are stacked (i.e.
+ // device-mapper).
+ if (alignment_offset == -1) {
+ alignment_offset = 0;
+ }
+
int logical_block_size;
if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {
- PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed";
+ PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed on " << block_device;
return false;
}
@@ -84,7 +92,7 @@
unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {
std::string path = GetPartitionAbsolutePath(partition_name);
- return unique_fd{open(path.c_str(), flags)};
+ return GetControlFileOrOpen(path.c_str(), flags | O_CLOEXEC);
}
bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index a02e746..dcee6d2 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -256,6 +256,10 @@
LERROR << "Logical partition has invalid attribute set.";
return nullptr;
}
+ if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
+ LERROR << "Logical partition first_extent_index + num_extents overflowed.";
+ return nullptr;
+ }
if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
LERROR << "Logical partition has invalid extent list.";
return nullptr;
@@ -348,6 +352,49 @@
return ParseMetadata(geometry, fd);
}
+namespace {
+
+bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
+ std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+ for (auto& partition : metadata->partitions) {
+ if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string partition_name = GetPartitionName(partition) + slot_suffix;
+ if (partition_name.size() > sizeof(partition.name)) {
+ LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+ return false;
+ }
+ strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));
+ partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;
+ }
+ for (auto& block_device : metadata->block_devices) {
+ if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
+ if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
+ LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+ return false;
+ }
+ block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+ }
+ for (auto& group : metadata->groups) {
+ if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {
+ continue;
+ }
+ std::string group_name = GetPartitionGroupName(group) + slot_suffix;
+ if (!UpdatePartitionGroupName(&group, group_name)) {
+ LERROR << __PRETTY_FUNCTION__ << " group name too long: " << group_name;
+ return false;
+ }
+ group.flags &= ~LP_GROUP_SLOT_SUFFIXED;
+ }
+ return true;
+}
+
+} // namespace
+
std::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,
const std::string& super_partition, uint32_t slot_number) {
android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);
@@ -360,18 +407,30 @@
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
return nullptr;
}
-
if (slot_number >= geometry.metadata_slot_count) {
LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
return nullptr;
}
- // Read the primary copy, and if that fails, try the backup.
- std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
- if (metadata) {
- return metadata;
+ std::vector<int64_t> offsets = {
+ GetPrimaryMetadataOffset(geometry, slot_number),
+ GetBackupMetadataOffset(geometry, slot_number),
+ };
+ std::unique_ptr<LpMetadata> metadata;
+
+ for (const auto& offset : offsets) {
+ if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+ PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset;
+ continue;
+ }
+ if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {
+ break;
+ }
}
- return ReadBackupMetadata(fd, geometry, slot_number);
+ if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {
+ return nullptr;
+ }
+ return metadata;
}
std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index d5d5188..7a2490b 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -38,7 +38,9 @@
bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
-// These functions assume a valid geometry and slot number.
+// These functions assume a valid geometry and slot number, and do not obey
+// auto-slot-suffixing. They are used for tests and for checking whether
+// the metadata is coherent across primary and backup copies.
std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
uint32_t slot_number);
std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 742ad82..338b525 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -19,16 +19,26 @@
#include <sys/stat.h>
#include <unistd.h>
+#if defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+
#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <openssl/sha.h>
+#ifdef __ANDROID__
+#include <cutils/android_get_control_file.h>
+#endif
+
#include "utility.h"
namespace android {
namespace fs_mgr {
bool GetDescriptorSize(int fd, uint64_t* size) {
+#if !defined(_WIN32)
struct stat s;
if (fstat(fd, &s) < 0) {
PERROR << __PRETTY_FUNCTION__ << "fstat failed";
@@ -39,6 +49,7 @@
*size = get_block_device_size(fd);
return *size != 0;
}
+#endif
int64_t result = SeekFile64(fd, 0, SEEK_END);
if (result == -1) {
@@ -97,15 +108,15 @@
}
uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
- if (suffix.empty()) {
+ if (suffix.empty() || suffix == "a" || suffix == "_a") {
return 0;
- }
- if (suffix.size() != 2 || suffix[0] != '_' || suffix[1] < 'a') {
+ } else if (suffix == "b" || suffix == "_b") {
+ return 1;
+ } else {
LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
<< "' does not have a recognized format.";
return 0;
}
- return suffix[1] - 'a';
}
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
@@ -116,5 +127,85 @@
return size;
}
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+ std::vector<std::string> list;
+ for (const auto& block_device : metadata.block_devices) {
+ list.emplace_back(GetBlockDevicePartitionName(block_device));
+ }
+ return list;
+}
+
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
+ for (const auto& partition : metadata.partitions) {
+ if (GetPartitionName(partition) == name) {
+ return &partition;
+ }
+ }
+ return nullptr;
+}
+
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
+ uint64_t total_size = 0;
+ for (uint32_t i = 0; i < partition.num_extents; i++) {
+ const auto& extent = metadata.extents[partition.first_extent_index + i];
+ total_size += extent.num_sectors * LP_SECTOR_SIZE;
+ }
+ return total_size;
+}
+
+std::string GetPartitionSlotSuffix(const std::string& partition_name) {
+ if (partition_name.size() <= 2) {
+ return "";
+ }
+ std::string suffix = partition_name.substr(partition_name.size() - 2);
+ return (suffix == "_a" || suffix == "_b") ? suffix : "";
+}
+
+std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
+ CHECK(slot_number == 0 || slot_number == 1);
+ return (slot_number == 0) ? "_a" : "_b";
+}
+
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
+ if (name.size() > sizeof(device->partition_name)) {
+ return false;
+ }
+ strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
+ return true;
+}
+
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
+ if (name.size() > sizeof(group->name)) {
+ return false;
+ }
+ strncpy(group->name, name.c_str(), sizeof(group->name));
+ return true;
+}
+
+bool SetBlockReadonly(int fd, bool readonly) {
+#if defined(__linux__)
+ int val = readonly;
+ return ioctl(fd, BLKROSET, &val) == 0;
+#else
+ (void)fd;
+ (void)readonly;
+ return true;
+#endif
+}
+
+base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+#if defined(__ANDROID__)
+ int fd = android_get_control_file(path);
+ if (fd >= 0) {
+ int newfd = TEMP_FAILURE_RETRY(dup(fd));
+ if (newfd >= 0) {
+ return base::unique_fd(newfd);
+ }
+ PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
+ }
+#endif
+ return base::unique_fd(open(path, flags));
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 65e643b..96f1717 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
@@ -29,6 +30,7 @@
#define LWARN LOG(WARNING) << LP_TAG
#define LINFO LOG(INFO) << LP_TAG
#define LERROR LOG(ERROR) << LP_TAG
+#define PWARNING PLOG(WARNING) << LP_TAG
#define PERROR PLOG(ERROR) << LP_TAG
namespace android {
@@ -84,6 +86,15 @@
return aligned;
}
+// Update names from C++ strings.
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+
+// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
+bool SetBlockReadonly(int fd, bool readonly);
+
+::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index bdf6dfd..15f7fff 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -24,10 +24,17 @@
TEST(liblp, SlotNumberForSlotSuffix) {
EXPECT_EQ(SlotNumberForSlotSuffix(""), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("a"), 0);
EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("b"), 1);
EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1);
- EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2);
- EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 0);
+ EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 0);
+}
+
+TEST(liblp, SlotSuffixForSlotNumber) {
+ EXPECT_EQ(SlotSuffixForSlotNumber(0), "_a");
+ EXPECT_EQ(SlotSuffixForSlotNumber(1), "_b");
}
TEST(liblp, GetMetadataOffset) {
@@ -60,3 +67,11 @@
EXPECT_EQ(AlignTo(32, 32, 30), 62);
EXPECT_EQ(AlignTo(17, 32, 30), 30);
}
+
+TEST(liblp, GetPartitionSlotSuffix) {
+ EXPECT_EQ(GetPartitionSlotSuffix("system"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("_"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("_a"), "");
+ EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
+ EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index c740bd4..bffcb7e 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -82,13 +82,8 @@
// Perform sanity checks so we don't accidentally overwrite valid metadata
// with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
- uint64_t blockdevice_size;
- if (!GetDescriptorSize(fd, &blockdevice_size)) {
- return false;
- }
-
- const LpMetadataHeader& header = metadata.header;
+static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const std::string& slot_suffix, std::string* blob) {
const LpMetadataGeometry& geometry = metadata.geometry;
*blob = SerializeMetadata(metadata);
@@ -104,7 +99,7 @@
// metadata.
uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
- uint64_t total_reserved = reserved_size * 2;
+ uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
if (!super_device) {
@@ -112,15 +107,36 @@
return false;
}
- if (total_reserved > blockdevice_size ||
- total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
- if (blockdevice_size != super_device->size) {
- LERROR << "Block device size " << blockdevice_size
- << " does not match metadata requested size " << super_device->size;
- return false;
+ for (const auto& block_device : metadata.block_devices) {
+ std::string partition_name = GetBlockDevicePartitionName(block_device);
+ if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {
+ if (slot_suffix.empty()) {
+ LERROR << "Block device " << partition_name << " requires a slot suffix,"
+ << " which could not be derived from the super partition name.";
+ return false;
+ }
+ partition_name += slot_suffix;
+ }
+
+ if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+ LERROR << "Block device " << partition_name << " has invalid first sector "
+ << block_device.first_logical_sector << " for size " << block_device.size;
+ return false;
+ }
+ BlockDeviceInfo info;
+ if (!opener.GetInfo(partition_name, &info)) {
+ PERROR << partition_name << ": ioctl";
+ return false;
+ }
+ if (info.size != block_device.size) {
+ LERROR << "Block device " << partition_name << " size mismatch (expected"
+ << block_device.size << ", got " << info.size << ")";
+ return false;
+ }
}
// Make sure all partition entries reference valid extents.
@@ -218,6 +234,10 @@
return android::base::WriteFully(fd, blob.data(), blob.size());
}
+#if defined(_WIN32)
+static const int O_SYNC = 0;
+#endif
+
bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,
const LpMetadata& metadata) {
android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);
@@ -226,14 +246,25 @@
return false;
}
+ // This is only used in update_engine and fastbootd, where the super
+ // partition should be specified as a name (or by-name link), and
+ // therefore, we should be able to extract a slot suffix.
+ std::string slot_suffix = GetPartitionSlotSuffix(super_partition);
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string metadata_blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &metadata_blob)) {
return false;
}
+ // On retrofit devices, super_partition is system_other and might be set to readonly by
+ // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.
+ if (!SetBlockReadonly(fd.get(), false)) {
+ PWARNING << __PRETTY_FUNCTION__ << " BLKROSET 0 failed: " << super_partition;
+ }
+
// Write zeroes to the first block.
std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
@@ -291,11 +322,13 @@
return false;
}
+ std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+
// Before writing geometry and/or logical partition tables, perform some
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &blob)) {
return false;
}
@@ -327,7 +360,7 @@
// synchronize the backup copy. This guarantees that a partial write
// still leaves one copy intact.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, *primary.get(), slot_suffix, &old_blob)) {
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
@@ -339,12 +372,12 @@
// The backup copy is coherent, and the primary is not. Sync it for
// safety.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
- LERROR << "Error serializing primary metadata to repair corrupted backup";
+ if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {
+ LERROR << "Error serializing backup metadata to repair corrupted primary";
return false;
}
if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
- LERROR << "Error writing primary metadata to repair corrupted backup";
+ LERROR << "Error writing backup metadata to repair corrupted primary";
return false;
}
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
new file mode 100644
index 0000000..3a08049
--- /dev/null
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+ name: "libsnapshot",
+ recovery_available: true,
+ defaults: ["fs_mgr_defaults"],
+ cppflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+ srcs: [
+ "snapshot.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libdm",
+ "libext2_uuid",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
new file mode 100644
index 0000000..0cfa7e4
--- /dev/null
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -0,0 +1,2 @@
+dvander@google.com
+elsk@google.com
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
new file mode 100644
index 0000000..5cfd7fa
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+
+namespace android {
+namespace snapshot {
+
+enum class UpdateStatus {
+ // No update or merge is in progress.
+ None,
+
+ // An update is pending, but has not been successfully booted yet.
+ Unverified,
+
+ // The kernel is merging in the background.
+ Merging,
+
+ // Merging is complete, and needs to be acknowledged.
+ MergeCompleted
+};
+
+class SnapshotManager final {
+ public:
+ // Return a new SnapshotManager instance, or null on error.
+ static std::unique_ptr<SnapshotManager> New();
+
+ // Create a new snapshot device with the given name, base device, and COW device
+ // size. The new device path will be returned in |dev_path|. If timeout_ms is
+ // greater than zero, this function will wait the given amount of time for
+ // |dev_path| to become available, and fail otherwise. If timeout_ms is 0, then
+ // no wait will occur and |dev_path| may not yet exist on return.
+ bool CreateSnapshot(const std::string& name, const std::string& base_device, uint64_t cow_size,
+ std::string* dev_path, const std::chrono::milliseconds& timeout_ms);
+
+ // Map a snapshot device that was previously created with CreateSnapshot.
+ // If a merge was previously initiated, the device-mapper table will have a
+ // snapshot-merge target instead of a snapshot target. The timeout parameter
+ // is the same as in CreateSnapshotDevice.
+ bool MapSnapshotDevice(const std::string& name, const std::string& base_device,
+ const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+
+ // Unmap a snapshot device previously mapped with MapSnapshotDevice().
+ bool UnmapSnapshotDevice(const std::string& name);
+
+ // Remove the backing copy-on-write image for the named snapshot. If the
+ // device is still mapped, this will attempt an Unmap, and fail if the
+ // unmap fails.
+ bool DeleteSnapshot(const std::string& name);
+
+ // Initiate a merge on all snapshot devices. This should only be used after an
+ // update has been marked successful after booting.
+ bool InitiateMerge();
+
+ // Wait for the current merge to finish, then perform cleanup when it
+ // completes. It is necessary to call this after InitiateMerge(), or when
+ // a merge is detected for the first time after boot.
+ bool WaitForMerge();
+
+ // Find the status of the current update, if any.
+ //
+ // |progress| depends on the returned status:
+ // None: 0
+ // Unverified: 0
+ // Merging: Value in the range [0, 100)
+ // MergeCompleted: 100
+ UpdateStatus GetUpdateStatus(double* progress);
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
new file mode 100644
index 0000000..3e80239
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -0,0 +1,72 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+std::unique_ptr<SnapshotManager> SnapshotManager::New() {
+ return std::make_unique<SnapshotManager>();
+}
+
+bool SnapshotManager::CreateSnapshot(const std::string& name, const std::string& base_device,
+ uint64_t cow_size, std::string* dev_path,
+ const std::chrono::milliseconds& timeout_ms) {
+ // (1) Create COW device using libgsi_image.
+ // (2) Create snapshot device using libdm + DmTargetSnapshot.
+ // (3) Record partition in /metadata/ota.
+ (void)name;
+ (void)base_device;
+ (void)cow_size;
+ (void)dev_path;
+ (void)timeout_ms;
+ return false;
+}
+
+bool SnapshotManager::MapSnapshotDevice(const std::string& name, const std::string& base_device,
+ const std::chrono::milliseconds& timeout_ms,
+ std::string* dev_path) {
+ (void)name;
+ (void)base_device;
+ (void)dev_path;
+ (void)timeout_ms;
+ return false;
+}
+
+bool SnapshotManager::UnmapSnapshotDevice(const std::string& name) {
+ (void)name;
+ return false;
+}
+
+bool SnapshotManager::DeleteSnapshot(const std::string& name) {
+ (void)name;
+ return false;
+}
+
+bool SnapshotManager::InitiateMerge() {
+ return false;
+}
+
+bool SnapshotManager::WaitForMerge() {
+ return false;
+}
+
+UpdateStatus SnapshotManager::GetUpdateStatus(double* progress) {
+ *progress = 0.0f;
+ return UpdateStatus::None;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 5497223..83668e9 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -14,6 +14,7 @@
cc_test {
name: "fs_mgr_unit_test",
+ test_suites: ["device-tests"],
shared_libs: [
"libbase",
@@ -23,8 +24,8 @@
"libfs_mgr",
"libfstab",
],
-
srcs: [
+ "file_wait_test.cpp",
"fs_mgr_test.cpp",
],
@@ -34,3 +35,40 @@
"-Werror",
],
}
+
+cc_prebuilt_binary {
+ name: "adb-remount-test.sh",
+ srcs: ["adb-remount-test.sh"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ android: {
+ enabled: false,
+ },
+ },
+ host_supported: true,
+}
+
+sh_test {
+ name: "adb-remount-sh",
+ src: "adb-remount-test.sh",
+ filename: "adb-remount-test.sh",
+ test_suites: ["general-tests"],
+ test_config: "adb-remount-sh.xml",
+}
+
+java_test_host {
+ name: "fs_mgr_vendor_overlay_test",
+
+ srcs: ["src/**/VendorOverlayHostTest.java"],
+
+ libs: ["tradefed"],
+
+ test_config: "vendor-overlay-test.xml",
+
+ test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
new file mode 100644
index 0000000..fa0d63f
--- /dev/null
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for adb remount test cases">
+ <option name="test-suite-tag" value="adb-remount" />
+ <!-- This test requires a device, so it's not annotated with a null-device -->
+ <test class="com.android.tradefed.testtype.binary.ExecutableHostTest" >
+ <option name="binary" value="adb-remount-test.sh" />
+ <!-- Increase default timeout as script is quite long -->
+ <option name="per-binary-timeout" value="1h" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index b6a8eef..642f2c1 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1,74 +1,185 @@
#! /bin/bash
+#
+# Divided into four section:
+#
+## USAGE
+## Helper Variables
+## Helper Functions
+## MAINLINE
-USAGE="USAGE: `basename ${0}` [-s <SerialNumber>]
+##
+## USAGE
+##
-adb remount tests (overlayfs focus)
+USAGE="USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]
+
+adb remount tests
+
+--color Dress output with highlighting colors
+--help This help
+--no-wait-screen Do not wait for display screen to settle
+--print-time Report the test duration
+--serial Specify device (must if multiple are present)
+--wait-adb <duration> adb wait timeout
+--wait-fastboot <duration> fastboot wait timeout
Conditions:
- Must be a userdebug build.
- Must be in adb mode.
- - Kernel must have overlayfs enabled and patched to support override_creds.
- - Must have either squashfs, ext4-dedupe or right-sized partitions.
- - Minimum expectation system and vender are overlayfs covered partitions.
+ - Also tests overlayfs
+ - Kernel must have overlayfs enabled and patched to support override_creds.
+ - Must have either erofs, squashfs, ext4-dedupe or full partitions.
+ - Minimum expectation system and vender are overlayfs covered partitions.
"
-if [ X"${1}" = X"--help" -o X"${1}" = X"-h" -o X"${1}" = X"-?" ]; then
- echo "${USAGE}" >&2
- exit 0
-fi
+##
+## Helper Variables
+##
-# Helper Variables
-
+EMPTY=""
SPACE=" "
# A _real_ embedded tab character
TAB="`echo | tr '\n' '\t'`"
# A _real_ embedded escape character
ESCAPE="`echo | tr '\n' '\033'`"
+# A _real_ embedded carriage return character
+CR="`echo | tr '\n' '\r'`"
GREEN="${ESCAPE}[38;5;40m"
RED="${ESCAPE}[38;5;196m"
ORANGE="${ESCAPE}[38;5;255:165:0m"
+BLUE="${ESCAPE}[35m"
NORMAL="${ESCAPE}[0m"
+TMPDIR=${TMPDIR:-/tmp}
+print_time=false
+start_time=`date +%s`
+ACTIVE_SLOT=
-# Helper functions
+ADB_WAIT=4m
+FASTBOOT_WAIT=2m
+screen_wait=true
+
+##
+## Helper Functions
+##
[ "USAGE: inFastboot
Returns: true if device is in fastboot mode" ]
inFastboot() {
- fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+ fastboot devices |
+ if [ -n "${ANDROID_SERIAL}" ]; then
+ grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+ else
+ wc -l | grep '^1$' >/dev/null
+ fi
}
[ "USAGE: inAdb
Returns: true if device is in adb mode" ]
inAdb() {
- adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+ adb devices |
+ grep -v -e 'List of devices attached' -e '^$' -e "[${SPACE}${TAB}]recovery\$" |
+ if [ -n "${ANDROID_SERIAL}" ]; then
+ grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+ else
+ wc -l | grep '^1$' >/dev/null
+ fi
}
-[ "USAGE: adb_sh <commands>
+[ "USAGE: inRecovery
+
+Returns: true if device is in recovery mode" ]
+inRecovery() {
+ local list="`adb devices |
+ grep -v -e 'List of devices attached' -e '^$'`"
+ if [ -n "${ANDROID_SERIAL}" ]; then
+ echo "${list}" |
+ grep "^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\$" >/dev/null
+ return ${?}
+ fi
+ if echo "${list}" | wc -l | grep '^1$' >/dev/null; then
+ echo "${list}" |
+ grep "[${SPACE}${TAB}]recovery\$" >/dev/null
+ return ${?}
+ fi
+ false
+}
+
+[ "USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command succeeded" ]
adb_sh() {
- adb shell "${@}"
+ local args=
+ for i in "${@}"; do
+ [ -z "${args}" ] || args="${args} "
+ if [ X"${i}" != X"${i#\'}" ]; then
+ args="${args}${i}"
+ elif [ X"${i}" != X"${i#* }" ]; then
+ args="${args}'${i}'"
+ elif [ X"${i}" != X"${i#*${TAB}}" ]; then
+ args="${args}'${i}'"
+ else
+ args="${args}${i}"
+ fi
+ done
+ adb shell "${args}"
+}
+
+[ "USAGE: adb_date >/dev/stdout
+
+Returns: report device epoch time (suitable for logcat -t)" ]
+adb_date() {
+ adb_sh date +%s.%N </dev/null
+}
+
+[ "USAGE: adb_logcat [arguments] >/dev/stdout
+
+Returns: the logcat output" ]
+adb_logcat() {
+ echo "${RED}[ INFO ]${NORMAL} logcat ${@}" >&2 &&
+ adb logcat "${@}" </dev/null |
+ tr -d '\r' |
+ grep -v 'logd : logdr: UID=' |
+ sed -e '${/------- beginning of kernel/d}' -e 's/^[0-1][0-9]-[0-3][0-9] //'
+}
+
+[ "USAGE: avc_check >/dev/stderr
+
+Returns: worrisome avc violations" ]
+avc_check() {
+ if ! ${overlayfs_supported:-false}; then
+ return
+ fi
+ local L=`adb_logcat -b all -v brief -d \
+ -e 'context=u:object_r:unlabeled:s0' 2>/dev/null |
+ sed -n 's/.*avc: //p' |
+ sort -u`
+ if [ -z "${L}" ]; then
+ return
+ fi
+ echo "${ORANGE}[ WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+ echo "${L}" |
+ sed 's/^/ /' >&2
}
[ "USAGE: get_property <prop>
Returns the property value" ]
get_property() {
- adb_sh getprop ${1} 2>&1 </dev/null
+ adb_sh getprop ${1} </dev/null
}
[ "USAGE: isDebuggable
Returns: true if device is (likely) a debug build" ]
isDebuggable() {
- if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then
+ if inAdb && [ 1 != "`get_property ro.debuggable`" ]; then
false
fi
}
-[ "USAGE: adb_su <commands>
+[ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
Returns: true if the command running as root succeeded" ]
adb_su() {
@@ -78,228 +189,1433 @@
[ "USAGE: adb_cat <file> >stdout
Returns: content of file to stdout with carriage returns skipped,
- true of the file exists" ]
+ true if the file exists" ]
adb_cat() {
- OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
- retval=${?}
+ local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
+ local ret=${?}
echo "${OUTPUT}" | tr -d '\r'
- return ${retval}
+ return ${ret}
+}
+
+[ "USAGE: adb_ls <dirfile> >stdout
+
+Returns: filename or directoru content to stdout with carriage returns skipped,
+ true if the ls had no errors" ]
+adb_ls() {
+ local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
+ local ret=${?}
+ echo "${OUTPUT}" | tr -d '\r'
+ return ${ret}
}
[ "USAGE: adb_reboot
Returns: true if the reboot command succeeded" ]
adb_reboot() {
- adb reboot remount-test
+ avc_check
+ adb reboot remount-test </dev/null || true
+ sleep 2
+}
+
+[ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+ if [ -z "${1}" ]; then
+ echo unknown
+ return
+ fi
+ local duration="${1}"
+ if [ X"${duration}" != X"${duration%s}" ]; then
+ duration=${duration%s}
+ elif [ X"${duration}" != X"${duration%m}" ]; then
+ duration=`expr ${duration%m} \* 60`
+ elif [ X"${duration}" != X"${duration%h}" ]; then
+ duration=`expr ${duration%h} \* 3600`
+ elif [ X"${duration}" != X"${duration%d}" ]; then
+ duration=`expr ${duration%d} \* 86400`
+ fi
+ local seconds=`expr ${duration} % 60`
+ local minutes=`expr \( ${duration} / 60 \) % 60`
+ local hours=`expr ${duration} / 3600`
+ if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
+ if [ 1 -eq ${duration} ]; then
+ echo 1 second
+ return
+ fi
+ echo ${duration} seconds
+ return
+ elif [ 60 -eq ${duration} ]; then
+ echo 1 minute
+ return
+ elif [ 0 -eq ${seconds} -a 0 -eq ${hours} ]; then
+ echo ${minutes} minutes
+ return
+ fi
+ if [ 0 -eq ${hours} ]; then
+ echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+ return
+ fi
+ echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+[ "USAGE: USB_DEVICE=\`usb_devnum [--next]\`
+
+USB_DEVICE contains cache. Update if system changes.
+
+Returns: the devnum for the USB_SERIAL device" ]
+usb_devnum() {
+ if [ -n "${USB_SERIAL}" ]; then
+ local usb_device=`cat ${USB_SERIAL%/serial}/devnum 2>/dev/null | tr -d ' \t\r\n'`
+ if [ -n "${usb_device}" ]; then
+ USB_DEVICE=dev${usb_device}
+ elif [ -n "${USB_DEVICE}" -a "${1}" ]; then
+ USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1`
+ fi
+ echo "${USB_DEVICE}"
+ fi
}
[ "USAGE: adb_wait [timeout]
-Returns: waits until the device has returned or the optional timeout" ]
+Returns: waits until the device has returned for adb or optional timeout" ]
adb_wait() {
+ local start=`date +%s`
+ local duration=
+ local ret
if [ -n "${1}" ]; then
- timeout --preserve-status --signal=KILL ${1} adb wait-for-device
+ USB_DEVICE=`usb_devnum --next`
+ duration=`format_duration ${1}`
+ echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
+ ret=${?}
+ echo -n " ${CR}"
else
adb wait-for-device
+ ret=${?}
fi
+ USB_DEVICE=`usb_devnum`
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+ fi
+ fi
+ local end=`date +%s`
+ local diff_time=`expr ${end} - ${start}`
+ local _print_time=${print_time}
+ if [ ${diff_time} -lt 15 ]; then
+ _print_time=false
+ fi
+ diff_time=`format_duration ${diff_time}`
+ if [ "${diff_time}" = "${duration}" ]; then
+ _print_time=false
+ fi
+
+ local reason=
+ if inAdb; then
+ reason=`get_property ro.boot.bootreason`
+ fi
+ case ${reason} in
+ reboot*)
+ reason=
+ ;;
+ ${EMPTY})
+ ;;
+ *)
+ reason=" for boot reason ${reason}"
+ ;;
+ esac
+ if ${_print_time} || [ -n "${reason}" ]; then
+ echo "${BLUE}[ INFO ]${NORMAL} adb wait duration ${diff_time}${reason}"
+ fi >&2
+
+ return ${ret}
+}
+
+[ "USAGE: adb_user > /dev/stdout
+
+Returns: the adb daemon user" ]
+adb_user() {
+ adb_sh echo '${USER}' </dev/null
+}
+
+[ "USAGE: usb_status > stdout 2> stderr
+
+Assumes referenced right after adb_wait or fastboot_wait failued.
+If wait failed, check if device is in adb, recovery or fastboot mode
+and report status strings like \"(USB stack borken?)\",
+\"(In fastboot mode)\", \"(In recovery mode)\" or \"(in adb mode)\".
+Additional diagnostics may be provided to the stderr output.
+
+Returns: USB status string" ]
+usb_status() {
+ if inFastboot; then
+ echo "(In fastboot mode)"
+ elif inRecovery; then
+ echo "(In recovery mode)"
+ elif inAdb; then
+ echo "(In adb mode `adb_user`)"
+ else
+ echo "(USB stack borken for ${USB_ADDRESS})"
+ USB_DEVICE=`usb_devnum`
+ if [ -n "${USB_DEVICE}" ]; then
+ echo "# lsusb -v -s ${USB_DEVICE#dev}"
+ local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`
+ if [ -n "${D}" ]; then
+ echo "${D}"
+ else
+ lsusb -v
+ fi
+ else
+ echo "# lsusb -v (expected device missing)"
+ lsusb -v
+ fi >&2
+ fi
+}
+
+[ "USAGE: fastboot_wait [timeout]
+
+Returns: waits until the device has returned for fastboot or optional timeout" ]
+fastboot_wait() {
+ local ret
+ # fastboot has no wait-for-device, but it does an automatic
+ # wait and requires (even a nonsensical) command to do so.
+ if [ -n "${1}" ]; then
+ USB_DEVICE=`usb_devnum --next`
+ echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+ timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
+ ret=${?}
+ echo -n " ${CR}"
+ ( exit ${ret} )
+ else
+ fastboot wait-for-device >/dev/null 2>/dev/null
+ fi ||
+ inFastboot
+ ret=${?}
+ USB_DEVICE=`usb_devnum`
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi >&2
+ fi
+ return ${ret}
+}
+
+[ "USAGE: recovery_wait [timeout]
+
+Returns: waits until the device has returned for recovery or optional timeout" ]
+recovery_wait() {
+ local ret
+ if [ -n "${1}" ]; then
+ USB_DEVICE=`usb_devnum --next`
+ echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+ timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
+ ret=${?}
+ echo -n " ${CR}"
+ else
+ adb wait-for-recovery
+ ret=${?}
+ fi
+ USB_DEVICE=`usb_devnum`
+ if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ fi >&2
+ fi
+ return ${ret}
+}
+
+[ "any_wait [timeout]
+
+Returns: waits until a device has returned or optional timeout" ]
+any_wait() {
+ (
+ adb_wait ${1} &
+ adb_pid=${!}
+ fastboot_wait ${1} &
+ fastboot_pid=${!}
+ recovery_wait ${1} &
+ recovery_pid=${!}
+ wait -n
+ kill "${adb_pid}" "${fastboot_pid}" "${recovery_pid}"
+ ) >/dev/null 2>/dev/null
+ inFastboot || inAdb || inRecovery
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+ if ! ${screen_wait}; then
+ adb_wait
+ return
+ fi
+ exit_function=true
+ if [ X"-n" = X"${1}" ]; then
+ exit_function=echo
+ shift
+ fi
+ timeout=${wait_for_screen_timeout}
+ if [ ${#} -gt 0 ]; then
+ timeout=${1}
+ shift
+ fi
+ counter=0
+ while true; do
+ if inFastboot; then
+ fastboot reboot
+ elif inAdb; then
+ if [ 0 != ${counter} ]; then
+ adb_wait
+ fi
+ if [ -n "`get_property sys.boot.reason`" ]
+ then
+ vals=`get_property |
+ sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+ if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+ then
+ sleep 1
+ break
+ fi
+ if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+ then
+ sleep 1
+ break
+ fi
+ fi
+ fi
+ counter=`expr ${counter} + 1`
+ if [ ${counter} -gt ${timeout} ]; then
+ ${exit_function}
+ echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ return 1
+ fi
+ sleep 1
+ done
+ ${exit_function}
}
[ "USAGE: adb_root
+NB: This can be flakey on devices due to USB state
+
Returns: true if device in root state" ]
adb_root() {
- adb root >/dev/null </dev/null 2>&1 &&
- sleep 1 &&
- adb_wait &&
- sleep 1
+ [ root != "`adb_user`" ] || return 0
+ adb root >/dev/null </dev/null 2>/dev/null
+ sleep 2
+ adb_wait ${ADB_WAIT} &&
+ [ root = "`adb_user`" ]
}
+[ "USAGE: adb_unroot
+
+NB: This can be flakey on devices due to USB state
+
+Returns: true if device in un root state" ]
+adb_unroot() {
+ [ root = "`adb_user`" ] || return 0
+ adb unroot >/dev/null </dev/null 2>/dev/null
+ sleep 2
+ adb_wait ${ADB_WAIT} &&
+ [ root != "`adb_user`" ]
+}
+
+[ "USAGE: fastboot_getvar var expected >/dev/stderr
+
+Returns: true if var output matches expected" ]
+fastboot_getvar() {
+ local O=`fastboot getvar ${1} 2>&1`
+ local ret=${?}
+ O="${O#< waiting for * >?}"
+ O="${O%%?Finished. Total time: *}"
+ if [ 0 -ne ${ret} ]; then
+ echo ${O} >&2
+ false
+ return
+ fi
+ if [ "${O}" != "${O#*FAILED}" ]; then
+ O="${1}: <empty>"
+ fi
+ if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
+ echo "${2} != ${O}"
+ false
+ return
+ fi >&2
+ echo ${O} >&2
+}
+
+[ "USAGE: get_active_slot >/dev/stdout
+
+Returns: with a or b string reporting active slot" ]
+get_active_slot() {
+ if inAdb || inRecovery; then
+ get_property ro.boot.slot_suffix | tr -d _
+ elif inFastboot; then
+ fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'
+ else
+ false
+ fi
+}
+
+[ "USAGE: restore
+
+Do nothing: should be redefined when necessary. Called after cleanup.
+
+Returns: reverses configurations" ]
+restore() {
+ true
+}
+
+[ "USAGE: cleanup
+
+Do nothing: should be redefined when necessary
+
+Returns: cleans up any latent resources" ]
+cleanup() {
+ true
+}
+
+[ "USAGE: test_duration >/dev/stderr
+
+Prints the duration of the test
+
+Returns: reports duration" ]
+test_duration() {
+ if ${print_time}; then
+ echo "${BLUE}[ INFO ]${NORMAL} end `date`"
+ [ -n "${start_time}" ] || return
+ end_time=`date +%s`
+ local diff_time=`expr ${end_time} - ${start_time}`
+ echo "${BLUE}[ INFO ]${NORMAL} duration `format_duration ${diff_time}`"
+ fi >&2
+}
+
+[ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
+
+If -d, or -t <epoch> argument is supplied, dump logcat.
+
+Returns: exit failure, report status" ]
die() {
+ if [ X"-d" = X"${1}" ]; then
+ adb_logcat -b all -v nsec -d
+ shift
+ elif [ X"-t" = X"${1}" ]; then
+ if [ -n "${2}" ]; then
+ adb_logcat -b all -v nsec -t ${2}
+ else
+ adb_logcat -b all -v nsec -d
+ fi
+ shift 2
+ fi >&2
echo "${RED}[ FAILED ]${NORMAL} ${@}" >&2
+ cleanup
+ restore
+ test_duration
exit 1
}
-[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
Returns true if (regex) lval matches rval" ]
EXPECT_EQ() {
- lval="${1}"
- rval="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
+ local error=1
+ local prefix="${RED}[ ERROR ]${NORMAL}"
+ if [ X"${1}" = X"--warning" ]; then
+ prefix="${RED}[ WARNING ]${NORMAL}"
+ error=0
+ shift 1
+ fi
if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
*}" ]; then
- echo "ERROR: expected \"${lval}\"" >&2
- echo " got \"${rval}\"" |
+ echo "${prefix} expected \"${lval}\""
+ echo "${prefix} got \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
- t again' >&2
+ t again'
if [ -n "${*}" ] ; then
- echo " ${*}" >&2
+ echo "${prefix} ${*}"
fi
else
- echo "ERROR: expected \"${lval}\" got \"${rval}\" ${*}" >&2
- fi
- return 1
+ echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
+ fi >&2
+ return ${error}
fi
if [ -n "${*}" ] ; then
- if [ X"${lval}" != X"${rval}" ]; then
+ prefix="${GREEN}[ INFO ]${NORMAL}"
+ if [ X"${lval}" != X"${rval}" ]; then # we were supplied a regex?
if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
- echo "INFO: ok \"${lval}\"" >&2
+ echo "${prefix} ok \"${lval}\""
echo " = \"${rval}\"" |
sed ': again
N
s/\(\n\)\([^ ]\)/\1 \2/
- t again' >&2
+ t again'
if [ -n "${*}" ] ; then
- echo " ${*}" >&2
+ echo "${prefix} ${*}"
fi
else
- echo "INFO: ok \"${lval}\" = \"${rval}\" ${*}" >&2
+ echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
fi
else
- echo "INFO: ok \"${lval}\" ${*}" >&2
- fi
+ echo "${prefix} ok \"${lval}\" ${*}"
+ fi >&2
fi
return 0
}
-[ "USAGE: check_eq <lval> <rval> [message]
+[ "USAGE: EXPECT_NE <lval> <rval> [--warning [message]]
+
+Returns true if lval matches rval" ]
+EXPECT_NE() {
+ local lval="${1}"
+ local rval="${2}"
+ shift 2
+ local error=1
+ local prefix="${RED}[ ERROR ]${NORMAL}"
+ if [ X"${1}" = X"--warning" ]; then
+ prefix="${RED}[ WARNING ]${NORMAL}"
+ error=0
+ shift 1
+ fi
+ if [ X"${rval}" = X"${lval}" ]; then
+ echo "${prefix} did not expect \"${lval}\" ${*}" >&2
+ return ${error}
+ fi
+ if [ -n "${*}" ] ; then
+ echo "${prefix} ok \"${lval}\" not \"${rval}\" ${*}" >&2
+ fi
+ return 0
+}
+
+[ "USAGE: check_eq <lval> <rval> [--warning [message]]
Exits if (regex) lval mismatches rval" ]
check_eq() {
- left="${1}"
- right="${2}"
+ local lval="${1}"
+ local rval="${2}"
shift 2
- EXPECT_EQ "${left}" "${right}" ||
+ if [ X"${1}" = X"--warning" ]; then
+ EXPECT_EQ "${lval}" "${rval}" ${*}
+ return
+ fi
+ EXPECT_EQ "${lval}" "${rval}" ||
die "${@}"
}
-[ "USAGE: skip_administrative_mounts
+[ "USAGE: check_ne <lval> <rval> [--warning [message]]
-Filters out all administrative (eg: sysfs) mounts" ]
-skip_administrative_mounts() {
- grep -v -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\|/data/media\) " -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|metadata\|data\) "
+Exits if lval matches rval" ]
+check_ne() {
+ local lval="${1}"
+ local rval="${2}"
+ shift 2
+ if [ X"${1}" = X"--warning" ]; then
+ EXPECT_NE "${lval}" "${rval}" ${*}
+ return
+ fi
+ EXPECT_NE "${lval}" "${rval}" ||
+ die "${@}"
}
-if [ X"-s" = X"${1}" -a -n "${2}" ]; then
- export ANDROID_SERIAL="${2}"
- shift 2
+[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
+
+Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
+skip_administrative_mounts() {
+ if [ "data" = "${1}" ]; then
+ grep -v " /data "
+ else
+ cat -
+ fi |
+ grep -v \
+ -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
+ -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
+ -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
+ -e "^rootfs / rootfs rw," \
+ -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
+}
+
+[ "USAGE: skip_unrelated_mounts < /proc/mounts
+
+or output from df
+
+Filters out all apex and vendor override administrative overlay mounts
+uninteresting to the test" ]
+skip_unrelated_mounts() {
+ grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
+ grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+}
+
+##
+## MAINLINE
+##
+
+OPTIONS=`getopt --alternative --unquoted \
+ --longoptions help,serial:,colour,color,no-colour,no-color \
+ --longoptions wait-adb:,wait-fastboot: \
+ --longoptions wait-screen,wait-display \
+ --longoptions no-wait-screen,no-wait-display \
+ --longoptions gtest_print_time,print-time \
+ -- "?hs:" ${*}` ||
+ ( echo "${USAGE}" >&2 ; false ) ||
+ die "getopt failure"
+set -- ${OPTIONS}
+
+color=false
+while [ ${#} -gt 0 ]; do
+ case ${1} in
+ -h | --help | -\?)
+ echo "${USAGE}" >&2
+ exit 0
+ ;;
+ -s | --serial)
+ export ANDROID_SERIAL=${2}
+ shift
+ ;;
+ --color | --colour)
+ color=true
+ ;;
+ --no-color | --no-colour)
+ color=false
+ ;;
+ --no-wait-display | --no-wait-screen)
+ screen_wait=false
+ ;;
+ --wait-display | --wait-screen)
+ screen_wait=true
+ ;;
+ --print-time | --gtest_print_time)
+ print_time=true
+ ;;
+ --wait-adb)
+ ADB_WAIT=${2}
+ shift
+ ;;
+ --wait-fastboot)
+ FASTBOOT_WAIT=${2}
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "${USAGE}" >&2
+ die "${0}: error unknown option ${1}"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+if ! ${color}; then
+ GREEN=""
+ RED=""
+ ORANGE=""
+ BLUE=""
+ NORMAL=""
+fi
+
+if ${print_time}; then
+ echo "${BLUE}[ INFO ]${NORMAL}" start `date` >&2
fi
inFastboot && die "device in fastboot mode"
+inRecovery && die "device in recovery mode"
if ! inAdb; then
- echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode ... waiting 2 minutes"
- adb_wait 2m
+ echo "${ORANGE}[ WARNING ]${NORMAL} device not in adb mode" >&2
+ adb_wait ${ADB_WAIT}
fi
-inAdb || die "device not in adb mode"
+inAdb || die "specified device not in adb mode"
isDebuggable || die "device not a debug build"
+enforcing=true
+if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+ enforcing=false
+fi
-# Do something
+# Do something.
+
+D=`get_property ro.serialno`
+[ -n "${D}" ] || D=`get_property ro.boot.serialno`
+[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
+USB_SERIAL=
+[ -z "${ANDROID_SERIAL}" ] || USB_SERIAL=`find /sys/devices -name serial |
+ grep usb |
+ xargs grep -l ${ANDROID_SERIAL}`
+USB_ADDRESS=
+if [ -n "${USB_SERIAL}" ]; then
+ USB_ADDRESS=${USB_SERIAL%/serial}
+ USB_ADDRESS=usb${USB_ADDRESS##*/}
+fi
+[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
+ USB_DEVICE=`usb_devnum`
+ echo "${BLUE}[ INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} >&2
+BUILD_DESCRIPTION=`get_property ro.build.description`
+[ -z "${BUILD_DESCRIPTION}" ] ||
+ echo "${BLUE}[ INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+ACTIVE_SLOT=`get_active_slot`
+[ -z "${ACTIVE_SLOT}" ] ||
+ echo "${BLUE}[ INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+
+# Report existing partition sizes
+adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+ sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
+ while read name device; do
+ case ${name} in
+ system_[ab] | system | vendor_[ab] | vendor | super | cache)
+ case ${device} in
+ sd*)
+ device=${device%%[0-9]*}/${device}
+ ;;
+ esac
+ size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+ size=`expr ${size} / 2` &&
+ echo "${BLUE}[ INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+ ;;
+ esac
+ done
+
+# If reboot too soon after fresh flash, could trip device update failure logic
+wait_for_screen
+# Can we test remount -R command?
+overlayfs_supported=true
+if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
+ "2" = "`get_property partition.system.verified`" ]; then
+ restore() {
+ ${overlayfs_supported} || return 0
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait ${ADB_WAIT}
+ inAdb &&
+ adb_root &&
+ adb enable-verity >/dev/null 2>/dev/null &&
+ adb_reboot &&
+ adb_wait ${ADB_WAIT}
+ }
+
+ echo "${GREEN}[ RUN ]${NORMAL} Testing adb shell su root remount -R command" >&2
+
+ avc_check
+ adb_su remount -R system </dev/null || true
+ sleep 2
+ adb_wait ${ADB_WAIT} ||
+ die "waiting for device after remount -R `usb_status`"
+ if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+ "2" = "`get_property partition.system.verified`" ]; then
+ die "remount -R command failed"
+ fi
+
+ echo "${GREEN}[ OK ]${NORMAL} adb shell su root remount -R command" >&2
+fi
+
+echo "${GREEN}[ RUN ]${NORMAL} Testing kernel support for overlayfs" >&2
+
adb_wait || die "wait for device failed"
-adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
-adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
- die "overlay module can not be used on ANDROID"
-adb_root &&
- adb_wait &&
- D=`adb disable-verity 2>&1` ||
- die "setup for overlay"
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null 2>/dev/null ||
+ adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
+ (
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay module not present" >&2 &&
+ false
+ ) ||
+ overlayfs_supported=false
+if ${overlayfs_supported}; then
+ adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null 2>/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
+ case `adb_sh uname -r </dev/null` in
+ 4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
+ overlayfs_supported=false
+ ;;
+ *)
+ echo "${GREEN}[ OK ]${NORMAL} overlay module uses caller's creds" >&2
+ ;;
+ esac
+fi
+
+adb_root ||
+ die "initial setup"
+
+echo "${GREEN}[ RUN ]${NORMAL} Checking current overlayfs status" >&2
+
+# We can not universally use adb enable-verity to ensure device is
+# in a overlayfs disabled state since it can prevent reboot on
+# devices that remount the physical content rather than overlayfs.
+# So lets do our best to surgically wipe the overlayfs state without
+# having to go through enable-verity transition.
+reboot=false
+OVERLAYFS_BACKING="cache mnt/scratch"
+for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
+ adb_sh rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
+ reboot=true
+ fi
+done
+if ${reboot}; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
+ adb_reboot &&
+ adb_wait ${ADB_WAIT} ||
+ die "lost device after reboot after wipe `usb_status`"
+ adb_root ||
+ die "lost device after elevation to root after wipe `usb_status`"
+fi
+D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
+overlayfs_needed=true
+D=`adb_sh cat /proc/mounts </dev/null |
+ skip_administrative_mounts data`
+if echo "${D}" | grep /dev/root >/dev/null; then
+ D=`echo / /
+ echo "${D}" | grep -v /dev/root`
+fi
+D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+no_dedupe=true
+for d in ${D}; do
+ adb_sh tune2fs -l $d </dev/null 2>&1 |
+ grep "Filesystem features:.*shared_blocks" >/dev/null &&
+ no_dedupe=false
+done
+D=`adb_sh df -k ${D} </dev/null |
+ sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
echo "${D}"
-if [ X"${D}" != X"${D##*using overlayfs}" ]; then
+if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
+ overlayfs_needed=false
+ # if device does not need overlays, then adb enable-verity will brick device
+ restore() {
+ ${overlayfs_supported} || return 0
+ inFastboot &&
+ fastboot reboot &&
+ adb_wait ${ADB_WAIT}
+ inAdb &&
+ adb_wait ${ADB_WAIT}
+ }
+elif ! ${overlayfs_supported}; then
+ die "need overlayfs, but do not have it"
+fi
+
+echo "${GREEN}[ RUN ]${NORMAL} disable verity" >&2
+
+T=`adb_date`
+H=`adb disable-verity 2>&1`
+err=${?}
+L=
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
fi
-if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
- echo "/data/overlay setup, clearing out" >&2
- adb_sh rm -rf /data/overlay </dev/null ||
- die "/data/overlay removal"
+if [ ${err} != 0 ]; then
+ echo "${H}"
+ ( [ -n "${L}" ] && echo "${L}" && false ) ||
+ die -t "${T}" "disable-verity"
fi
-adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
- adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
- die "overlay directory setup"
-adb_reboot &&
- adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " ||
- die "overlay takeover failed"
-adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
+rebooted=false
+if [ X"${D}" != X"${H}" ]; then
+ echo "${H}"
+ if [ X"${D}" != X"${D##*setup failed}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlayfs setup whined" >&2
+ fi
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
+ [ -z "${D}" ] ||
+ ( echo "${H}" && echo "${D}" && false ) ||
+ die -t ${T} "overlay takeover unexpected at this phase"
+ echo "${GREEN}[ INFO ]${NORMAL} rebooting as requested" >&2
+ L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
+ adb_reboot &&
+ adb_wait ${ADB_WAIT} ||
+ die "lost device after reboot requested `usb_status`"
+ adb_root ||
+ die "lost device after elevation to root `usb_status`"
+ rebooted=true
+ # re-disable verity to see the setup remarks expected
+ T=`adb_date`
+ H=`adb disable-verity 2>&1`
+ err=${?}
+ D="${H%?Now reboot your device for settings to take effect*}"
+ if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
+ echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
+ fi
+ if [ ${err} != 0 ]; then
+ T=
+ fi
+fi
+if ${overlayfs_supported} && ${overlayfs_needed} && [ X"${D}" != X"${D##*setup failed}" ]; then
+ echo "${D}"
+ ( [ -n "${L}" ] && echo "${L}" && false ) ||
+ die -t "${T}" "setup for overlay"
+fi
+if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
+ echo "${H}"
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
+ [ -z "${D}" ] ||
+ ( echo "${H}" && echo "${D}" && false ) ||
+ ( [ -n "${L}" ] && echo "${L}" && false ) ||
+ die -t "${T}" "overlay takeover unexpected"
+ [ -n "${L}" ] && echo "${L}"
+ die -t "${T}" "unexpected report of verity being disabled a second time"
+elif ${rebooted}; then
+ echo "${GREEN}[ OK ]${NORMAL} verity already disabled" >&2
+else
+ echo "${ORANGE}[ WARNING ]${NORMAL} verity already disabled" >&2
+fi
-adb_root &&
- adb_wait &&
- adb remount &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
- die "overlay takeover after remount"
-!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
- !(adb_sh grep " rw," /proc/mounts </dev/null |
- skip_administrative_mounts) ||
- die "remount overlayfs missed a spot"
+echo "${GREEN}[ RUN ]${NORMAL} remount" >&2
-adb_su "sed -n '1,/overlay \\/system/p' /proc/mounts" </dev/null |
- skip_administrative_mounts |
- grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
- echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
- echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
+# Feed log with selinux denials as baseline before overlays
+adb_unroot
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_root
-# Check something
+D=`adb remount 2>&1`
+ret=${?}
+echo "${D}"
+[ ${ret} != 0 ] ||
+ [ X"${D}" = X"${D##*remount failed}" ] ||
+ ( [ -n "${L}" ] && echo "${L}" && false ) ||
+ die -t "${T}" "adb remount failed"
+D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
+ ( [ -n "${L}" ] && echo "${L}" && false )
+ret=${?}
+uses_dynamic_scratch=false
+scratch_partition=
+if ${overlayfs_needed}; then
+ if [ ${ret} != 0 ]; then
+ die -t ${T} "overlay takeover failed"
+ fi
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover not complete" >&2
+ scratch_partition=scratch
+ if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+ echo "${BLUE}[ INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
+ fi
+ M=`adb_sh cat /proc/mounts </dev/null |
+ sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+ [ -n "${M}" ] &&
+ echo "${BLUE}[ INFO ]${NORMAL} scratch filesystem ${M}"
+ uses_dynamic_scratch=true
+ if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
+ uses_dynamic_scratch=false
+ scratch_partition="${M##*/dev/block/by-name/}"
+ fi
+ scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
+ while read device kblocks used available use mounted on; do
+ if [ "/mnt/scratch" = "\${mounted}" ]; then
+ echo \${kblocks}
+ fi
+ done` &&
+ [ -n "${scratch_size}" ] ||
+ die "scratch size"
+ echo "${BLUE}[ INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
+ for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>/dev/null; then
+ echo "${BLUE}[ INFO ]${NORMAL} /${d}/overlay is setup" >&2
+ fi
+ done
+
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+ die "overlay takeover after remount"
+ !(adb_sh grep "^overlay " /proc/mounts </dev/null |
+ skip_unrelated_mounts |
+ grep " overlay ro,") ||
+ die "remount overlayfs missed a spot (ro)"
+ D=`adb_sh grep " rw," /proc/mounts </dev/null |
+ skip_administrative_mounts data`
+ if echo "${D}" | grep /dev/root >/dev/null; then
+ D=`echo / /
+ echo "${D}" | grep -v /dev/root`
+ fi
+ D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+ bad_rw=false
+ for d in ${D}; do
+ if adb_sh tune2fs -l $d </dev/null 2>&1 |
+ grep "Filesystem features:.*shared_blocks" >/dev/null; then
+ bad_rw=true
+ else
+ d=`adb_sh df -k ${D} </dev/null |
+ sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
+ [ X"${d}" = X"${d##* 100[%] }" ] ||
+ bad_rw=true
+ fi
+ done
+ [ -z "${D}" ] ||
+ D=`adb_sh df -k ${D} </dev/null |
+ sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
+ -e 's/^Filesystem /Filesystem (rw) /'`
+ [ -z "${D}" ] || echo "${D}"
+ ${bad_rw} && die "remount overlayfs missed a spot (rw)"
+else
+ if [ ${ret} = 0 ]; then
+ die -t ${T} "unexpected overlay takeover"
+ fi
+fi
+
+# Check something.
+
+echo "${GREEN}[ RUN ]${NORMAL} push content to /system and /vendor" >&2
+
A="Hello World! $(date)"
-echo "${A}" | adb_sh "cat - > /system/hello"
-echo "${A}" | adb_sh "cat - > /vendor/hello"
+echo "${A}" | adb_sh cat - ">/system/hello"
+echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
+echo "${A}" | adb_sh cat - ">/vendor/hello"
B="`adb_cat /system/hello`" ||
- die "sytem hello"
-check_eq "${A}" "${B}" system before reboot
+ die "system hello"
+check_eq "${A}" "${B}" /system before reboot
+B="`adb_cat /system/priv-app/hello`" ||
+ die "system priv-app hello"
+check_eq "${A}" "${B}" /system/priv-app before reboot
B="`adb_cat /vendor/hello`" ||
die "vendor hello"
-check_eq "${A}" "${B}" vendor before reboot
+check_eq "${A}" "${B}" /vendor before reboot
+SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
+VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
+SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
+VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
+BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
+BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
+check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
+check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
+if ${overlayfs_needed}; then
+ check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+ check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+else
+ check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
+ check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
+fi
+check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
+[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+ die "system devt ${SYSTEM_DEVT} is major 0"
+[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
+ die "vendor devt ${SYSTEM_DEVT} is major 0"
+
+# Download libc.so, append some gargage, push back, and check if the file
+# is updated.
+tempdir="`mktemp -d`"
+cleanup() {
+ rm -rf ${tempdir}
+}
+adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
+ die "pull libc.so from device"
+garbage="`hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random`"
+echo ${garbage} >> ${tempdir}/libc.so
+adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
+ die "push libc.so to device"
+adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
+ die "pull libc.so from device"
+diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null ||
+ die "libc.so differ"
+
+echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
+
+fixup_from_recovery() {
+ inRecovery || return 1
+ echo "${ORANGE}[ ERROR ]${NORMAL} Device in recovery" >&2
+ adb reboot </dev/null
+ adb_wait ${ADB_WAIT}
+}
+
adb_reboot &&
- adb_wait &&
- B="`adb_cat /system/hello`" ||
- die "re-read system hello after reboot"
-check_eq "${A}" "${B}" system after reboot
-# Only root can read vendor if sepolicy permissions are as expected
-B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after reboot w/o root"
-check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
-adb_root &&
- adb_wait &&
- B="`adb_cat /vendor/hello`" ||
- die "re-read vendor hello after reboot"
-check_eq "${A}" "${B}" vendor after reboot
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_recovery ||
+ die "reboot after override content added failed `usb_status`"
-adb reboot-fastboot &&
- fastboot flash vendor &&
- fastboot reboot ||
- die "fastbootd flash vendor"
-adb_wait &&
- adb_root &&
- adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
- die "overlay system takeover after flash vendor"
-adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
- die "overlay minus vendor takeover after flash vendor"
-B="`adb_cat /system/hello`" ||
- die "re-read system hello after flash vendor"
-check_eq "${A}" "${B}" system after flash vendor
-adb_root &&
- adb_wait ||
+if ${overlayfs_needed}; then
+ D=`adb_su df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+ ( echo "${L}" && false ) ||
+ die -d "overlay takeover failed after reboot"
+
+ adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
+ skip_administrative_mounts |
+ grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
+ echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
+fi
+
+if ${enforcing}; then
+ adb_unroot ||
+ die "device not in unroot'd state"
+ B="`adb_cat /vendor/hello 2>&1`"
+ check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
+ echo "${GREEN}[ OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
+ # Feed unprivileged log with selinux denials as a result of overlays
+ wait_for_screen
+ adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+fi
+B="`adb_cat /system/hello`"
+check_eq "${A}" "${B}" /system after reboot
+# If overlayfs has a nested security problem, this will fail.
+B="`adb_ls /system/`" ||
+ dir "adb ls /system"
+[ X"${B}" != X"${B#*priv-app}" ] ||
+ dir "adb ls /system/priv-app"
+B="`adb_cat /system/priv-app/hello`"
+check_eq "${A}" "${B}" /system/priv-app after reboot
+echo "${GREEN}[ OK ]${NORMAL} /system content remains after reboot" >&2
+# Only root can read vendor if sepolicy permissions are as expected.
+adb_root ||
die "adb root"
-B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after flash vendor"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" vendor after flash vendor
+B="`adb_cat /vendor/hello`"
+check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
-adb remount &&
+check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
+check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" base system devt after reboot
+check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
+# Feed log with selinux denials as a result of overlays
+adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+
+# Check if the updated libc.so is persistent after reboot.
+adb_root &&
+ adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
+ die "pull libc.so from device"
+diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
+rm -rf ${tempdir}
+cleanup() {
+ true
+}
+echo "${GREEN}[ OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
+
+echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2
+
+H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
+is_bootloader_fastboot=false
+# cuttlefish?
+[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
+is_userspace_fastboot=false
+
+if ! ${is_bootloader_fastboot}; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} does not support fastboot, skipping"
+elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} build tree not setup, skipping"
+elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} vendor image missing, skipping"
+elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} wrong vendor image, skipping"
+elif [ -z "${ANDROID_HOST_OUT}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
+elif ! (
+ adb_cat /vendor/build.prop |
+ cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
+ ) >/dev/null 2>/dev/null; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+else
+ wait_for_screen
+ avc_check
+ adb reboot fastboot </dev/null ||
+ die "fastbootd not supported (wrong adb in path?)"
+ any_wait ${ADB_WAIT} &&
+ inFastboot ||
+ die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
+ fastboot flash vendor ||
+ ( fastboot reboot && false) ||
+ die "fastboot flash vendor"
+ fastboot_getvar is-userspace yes &&
+ is_userspace_fastboot=true
+ if [ -n "${scratch_paritition}" ]; then
+ fastboot_getvar partition-type:${scratch_partition} raw ||
+ ( fastboot reboot && false) ||
+ die "fastboot can not see ${scratch_partition} parameters"
+ if ${uses_dynamic_scratch}; then
+ # check ${scratch_partition} via fastboot
+ fastboot_getvar has-slot:${scratch_partition} no &&
+ fastboot_getvar is-logical:${scratch_partition} yes ||
+ ( fastboot reboot && false) ||
+ die "fastboot can not see ${scratch_partition} parameters"
+ else
+ fastboot_getvar is-logical:${scratch_partition} no ||
+ ( fastboot reboot && false) ||
+ die "fastboot can not see ${scratch_partition} parameters"
+ fi
+ if ! ${uses_dynamic_scratch}; then
+ fastboot reboot-bootloader ||
+ die "Reboot into fastboot"
+ fi
+ if ${uses_dynamic_scratch}; then
+ echo "${BLUE}[ INFO ]${NORMAL} expect fastboot erase ${scratch_partition} to fail" >&2
+ fastboot erase ${scratch_partition} &&
+ ( fastboot reboot || true) &&
+ die "fastboot can erase ${scratch_partition}"
+ fi
+ echo "${BLUE}[ INFO ]${NORMAL} expect fastboot format ${scratch_partition} to fail" >&2
+ fastboot format ${scratch_partition} &&
+ ( fastboot reboot || true) &&
+ die "fastboot can format ${scratch_partition}"
+ fi
+ fastboot reboot ||
+ die "can not reboot out of fastboot"
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb after fastboot"
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_recovery ||
+ die "did not reboot after formatting ${scratch_partition} `usb_status`"
+ if ${overlayfs_needed}; then
+ adb_root &&
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+ die "overlay /system takeover after flash vendor"
+ echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+ if ${is_userspace_fastboot}; then
+ die "overlay supposed to be minus /vendor takeover after flash vendor"
+ else
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+ fi
+ fi
+ B="`adb_cat /system/hello`"
+ check_eq "${A}" "${B}" system after flash vendor
+ B="`adb_ls /system/`" ||
+ dir "adb ls /system"
+ [ X"${B}" != X"${B#*priv-app}" ] ||
+ dir "adb ls /system/priv-app"
+ B="`adb_cat /system/priv-app/hello`"
+ check_eq "${A}" "${B}" system/priv-app after flash vendor
+ adb_root ||
+ die "adb root"
+ B="`adb_cat /vendor/hello`"
+ if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ vendor content after flash vendor
+ else
+ echo "${ORANGE}[ WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
+ check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
+ --warning vendor content after flash vendor
+ fi
+
+ check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
+ check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" base system devt after reboot
+ check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
+
+fi
+
+wait_for_screen
+echo "${GREEN}[ RUN ]${NORMAL} remove test content (cleanup)" >&2
+
+T=`adb_date`
+H=`adb remount 2>&1`
+err=${?}
+L=
+D="${H%?Now reboot your device for settings to take effect*}"
+if [ X"${H}" != X"${D}" ]; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
+ L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
+ adb_reboot &&
+ adb_wait ${ADB_WAIT} &&
+ adb_root ||
+ die "failed to reboot"
+ T=`adb_date`
+ H=`adb remount 2>&1`
+ err=${?}
+fi
+echo "${H}"
+[ ${err} = 0 ] &&
( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
- adb_sh rm /system/hello </dev/null ||
- die "cleanup hello"
-B="`adb_cat /system/hello`" &&
- die "re-read system hello after rm"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after flash rm
-B="`adb_cat /vendor/hello`" &&
- die "re-read vendor hello after rm"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after flash rm
+ adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
+ ( [ -n "${L}" ] && echo "${L}" && false ) ||
+ die -t ${T} "cleanup hello"
+B="`adb_cat /system/hello`"
+check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /system/priv-app/hello`"
+check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /vendor/hello`"
+check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
+
+if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
+
+ echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
+
+ avc_check
+ adb reboot fastboot </dev/null ||
+ die "Reboot into fastbootd"
+ img=${TMPDIR}/adb-remount-test-${$}.img
+ cleanup() {
+ rm ${img}
+ }
+ dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
+ fastboot_wait ${FASTBOOT_WAIT} ||
+ die "reboot into fastboot `usb_status`"
+ fastboot flash --force ${scratch_partition} ${img}
+ err=${?}
+ cleanup
+ cleanup() {
+ true
+ }
+ fastboot reboot ||
+ die "can not reboot out of fastboot"
+ [ 0 -eq ${err} ] ||
+ die "fastboot flash ${scratch_partition}"
+ adb_wait ${ADB_WAIT} &&
+ adb_root ||
+ die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+ T=`adb_date`
+ D=`adb disable-verity 2>&1`
+ err=${?}
+ if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
+ then
+ echo "${ORANGE}[ WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+ adb_reboot &&
+ adb_wait ${ADB_WAIT} &&
+ adb_root ||
+ die "failed to reboot"
+ T=`adb_date`
+ D="${D}
+`adb disable-verity 2>&1`"
+ err=${?}
+ fi
+
+ echo "${D}"
+ [ ${err} = 0 ] &&
+ [ X"${D}" = X"${D##*setup failed}" ] &&
+ [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
+ echo "${GREEN}[ OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
+ die -t ${T} "setup for overlayfs"
+ D=`adb remount 2>&1`
+ err=${?}
+ echo "${D}"
+ [ ${err} != 0 ] ||
+ [ X"${D}" = X"${D##*remount failed}" ] ||
+ ( echo "${D}" && false ) ||
+ die -t ${T} "remount failed"
+fi
+
+echo "${GREEN}[ RUN ]${NORMAL} test raw remount commands" >&2
+
+fixup_from_fastboot() {
+ inFastboot || return 1
+ if [ -n "${ACTIVE_SLOT}" ]; then
+ local active_slot=`get_active_slot`
+ if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+ else
+ echo "${ORANGE}[ ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
+ fi >&2
+ fastboot --set-active=${ACTIVE_SLOT}
+ fi
+ fastboot reboot
+ adb_wait ${ADB_WAIT}
+}
+
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
+ die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+adb_su mount -o rw,remount /vendor </dev/null ||
+ die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+ die "/vendor is not read-write"
+echo "${GREEN}[ OK ]${NORMAL} mount -o rw,remount command works" >&2
+
+# Prerequisite is a prepped device from above.
+adb_reboot &&
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
+ die "lost device after reboot to ro state `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+ die "remount command"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+ die "/vendor is not read-write"
+adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+echo "${GREEN}[ OK ]${NORMAL} remount command works from setup" >&2
+
+# Prerequisite is an overlayfs deconstructed device but with verity disabled.
+# This also saves a lot of 'noise' from the command doing a mkfs on backing
+# storage and all the related tuning and adjustment.
+for d in ${OVERLAYFS_BACKING}; do
+ adb_su rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
+done
+adb_reboot &&
+ adb_wait ${ADB_WAIT} ||
+ fixup_from_fastboot ||
+ die "lost device after reboot after wipe `usb_status`"
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/vendor is not read-only"
+adb_su remount vendor </dev/null ||
+ die "remount command"
+adb_su df -k </dev/null | skip_unrelated_mounts
+adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
+ die "/vendor is not read-write"
+adb_sh grep " \(/system\|/\) .* rw," /proc/mounts >/dev/null </dev/null &&
+ die "/system is not read-only"
+echo "${GREEN}[ OK ]${NORMAL} remount command works from scratch" >&2
+
+if ! restore; then
+ restore() {
+ true
+ }
+ die "failed to restore verity after remount from scratch test"
+fi
+
+err=0
+
+if ${overlayfs_supported}; then
+ echo "${GREEN}[ RUN ]${NORMAL} test 'adb remount -R'" >&2
+ avc_check
+ adb_root &&
+ adb remount -R &&
+ adb_wait ${ADB_WAIT} ||
+ die "adb remount -R"
+ if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+ "2" = "`get_property partition.system.verified`" ] &&
+ [ -n "`get_property ro.boot.verifiedbootstate`" -o \
+ -n "`get_property partition.system.verified`" ]; then
+ die "remount -R command failed to disable verity"
+ fi
+
+ echo "${GREEN}[ OK ]${NORMAL} 'adb remount -R' command" >&2
+
+ restore
+ err=${?}
+fi
+
+restore() {
+ true
+}
+
+[ ${err} = 0 ] ||
+ die "failed to restore verity"
echo "${GREEN}[ PASSED ]${NORMAL} adb remount" >&2
+
+test_duration
diff --git a/fs_mgr/tests/file_wait_test.cpp b/fs_mgr/tests/file_wait_test.cpp
new file mode 100644
index 0000000..cc8b143
--- /dev/null
+++ b/fs_mgr/tests/file_wait_test.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <chrono>
+#include <string>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr/file_wait.h>
+#include <gtest/gtest.h>
+
+using namespace std::literals;
+using android::base::unique_fd;
+using android::fs_mgr::WaitForFile;
+using android::fs_mgr::WaitForFileDeleted;
+
+class FileWaitTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ test_file_ = temp_dir_.path + "/"s + tinfo->name();
+ }
+
+ void TearDown() override { unlink(test_file_.c_str()); }
+
+ TemporaryDir temp_dir_;
+ std::string test_file_;
+};
+
+TEST_F(FileWaitTest, FileExists) {
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ ASSERT_GE(fd, 0);
+
+ ASSERT_TRUE(WaitForFile(test_file_, 500ms));
+ ASSERT_FALSE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, FileDoesNotExist) {
+ ASSERT_FALSE(WaitForFile(test_file_, 500ms));
+ ASSERT_TRUE(WaitForFileDeleted(test_file_, 500ms));
+}
+
+TEST_F(FileWaitTest, CreateAsync) {
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ });
+ EXPECT_TRUE(WaitForFile(test_file_, 3s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, CreateOtherAsync) {
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ });
+ EXPECT_FALSE(WaitForFile(test_file_ + ".wontexist", 2s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, DeleteAsync) {
+ // Note: need to close the file, otherwise inotify considers it not deleted.
+ {
+ unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));
+ ASSERT_GE(fd, 0);
+ }
+
+ std::thread thread([this] {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ unlink(test_file_.c_str());
+ });
+ EXPECT_TRUE(WaitForFileDeleted(test_file_, 3s));
+ thread.join();
+}
+
+TEST_F(FileWaitTest, BadPath) {
+ ASSERT_FALSE(WaitForFile("/this/path/does/not/exist", 5ms));
+ EXPECT_EQ(errno, ENOENT);
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index db01c1e..6d87594 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -24,12 +24,16 @@
#include <utility>
#include <vector>
+#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fstab/fstab.h>
#include <gtest/gtest.h>
#include "../fs_mgr_priv_boot_config.h"
+using namespace android::fs_mgr;
+
namespace {
const std::string cmdline =
@@ -136,38 +140,32 @@
}
TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
- auto fstab = fs_mgr_read_fstab("/proc/mounts");
- ASSERT_NE(fstab, nullptr);
+ Fstab fstab;
+ ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent("/proc/mounts", "re"),
endmntent);
ASSERT_NE(mounts, nullptr);
mntent* mentry;
- int i = 0;
+ size_t i = 0;
while ((mentry = getmntent(mounts.get())) != nullptr) {
- ASSERT_LT(i, fstab->num_entries);
- auto fsrec = &fstab->recs[i];
+ ASSERT_LT(i, fstab.size());
+ auto& entry = fstab[i];
- std::string mnt_fsname(mentry->mnt_fsname ?: "nullptr");
- std::string blk_device(fsrec->blk_device ?: "nullptr");
- EXPECT_EQ(mnt_fsname, blk_device);
-
- std::string mnt_dir(mentry->mnt_dir ?: "nullptr");
- std::string mount_point(fsrec->mount_point ?: "nullptr");
- EXPECT_EQ(mnt_dir, mount_point);
-
- std::string mnt_type(mentry->mnt_type ?: "nullptr");
- std::string fs_type(fsrec->fs_type ?: "nullptr");
- EXPECT_EQ(mnt_type, fs_type);
+ EXPECT_EQ(mentry->mnt_fsname, entry.blk_device);
+ EXPECT_EQ(mentry->mnt_dir, entry.mount_point);
+ EXPECT_EQ(mentry->mnt_type, entry.fs_type);
std::set<std::string> mnt_opts;
- for (auto& s : android::base::Split(mentry->mnt_opts ?: "nullptr", ",")) {
+ for (auto& s : android::base::Split(mentry->mnt_opts, ",")) {
mnt_opts.emplace(s);
}
std::set<std::string> fs_options;
- for (auto& s : android::base::Split(fsrec->fs_options ?: "nullptr", ",")) {
- fs_options.emplace(s);
+ if (!entry.fs_options.empty()) {
+ for (auto& s : android::base::Split(entry.fs_options, ",")) {
+ fs_options.emplace(s);
+ }
}
// matches private content in fs_mgr_fstab.c
static struct flag_list {
@@ -192,12 +190,849 @@
{0, 0},
};
for (auto f = 0; mount_flags[f].name; ++f) {
- if (mount_flags[f].flag & fsrec->flags) {
+ if (mount_flags[f].flag & entry.flags) {
fs_options.emplace(mount_flags[f].name);
}
}
- if (!(fsrec->flags & MS_RDONLY)) fs_options.emplace("rw");
+ if (!(entry.flags & MS_RDONLY)) {
+ fs_options.emplace("rw");
+ }
EXPECT_EQ(mnt_opts, fs_options);
++i;
}
+ EXPECT_EQ(i, fstab.size());
+}
+
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_MountOptions) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source / ext4 ro,barrier=1 wait,slotselect,avb
+source /metadata ext4 noatime,nosuid,nodev,discard wait,formattable
+
+source /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
+
+source /misc emmc defaults defaults
+
+source /vendor/firmware_mnt vfat ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect
+
+source auto vfat defaults voldmanaged=usb:auto
+source none swap defaults zramsize=1073741824,max_comp_streams=8
+source none2 swap nodiratime,remount,bind zramsize=1073741824,max_comp_streams=8
+source none3 swap unbindable,private,slave zramsize=1073741824,max_comp_streams=8
+source none4 swap noexec,shared,rec zramsize=1073741824,max_comp_streams=8
+source none5 swap rw zramsize=1073741824,max_comp_streams=8
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(11U, fstab.size());
+
+ EXPECT_EQ("/", fstab[0].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[0].flags);
+ EXPECT_EQ("barrier=1", fstab[0].fs_options);
+
+ EXPECT_EQ("/metadata", fstab[1].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[1].flags);
+ EXPECT_EQ("discard", fstab[1].fs_options);
+
+ EXPECT_EQ("/data", fstab[2].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), fstab[2].flags);
+ EXPECT_EQ("discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier", fstab[2].fs_options);
+
+ EXPECT_EQ("/misc", fstab[3].mount_point);
+ EXPECT_EQ(0U, fstab[3].flags);
+ EXPECT_EQ("", fstab[3].fs_options);
+
+ EXPECT_EQ("/vendor/firmware_mnt", fstab[4].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), fstab[4].flags);
+ EXPECT_EQ(
+ "shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,"
+ "context=u:object_r:firmware_file:s0",
+ fstab[4].fs_options);
+
+ EXPECT_EQ("auto", fstab[5].mount_point);
+ EXPECT_EQ(0U, fstab[5].flags);
+ EXPECT_EQ("", fstab[5].fs_options);
+
+ EXPECT_EQ("none", fstab[6].mount_point);
+ EXPECT_EQ(0U, fstab[6].flags);
+ EXPECT_EQ("", fstab[6].fs_options);
+
+ EXPECT_EQ("none2", fstab[7].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NODIRATIME | MS_REMOUNT | MS_BIND), fstab[7].flags);
+ EXPECT_EQ("", fstab[7].fs_options);
+
+ EXPECT_EQ("none3", fstab[8].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE), fstab[8].flags);
+ EXPECT_EQ("", fstab[8].fs_options);
+
+ EXPECT_EQ("none4", fstab[9].mount_point);
+ EXPECT_EQ(static_cast<unsigned long>(MS_NOEXEC | MS_SHARED | MS_REC), fstab[9].flags);
+ EXPECT_EQ("", fstab[9].fs_options);
+
+ EXPECT_EQ("none5", fstab[10].mount_point);
+ EXPECT_EQ(0U, fstab[10].flags); // rw is the same as defaults
+ EXPECT_EQ("", fstab[10].fs_options);
+}
+
+static bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
+ // clang-format off
+ return lhs.wait == rhs.wait &&
+ lhs.check == rhs.check &&
+ lhs.crypt == rhs.crypt &&
+ lhs.nonremovable == rhs.nonremovable &&
+ lhs.vold_managed == rhs.vold_managed &&
+ lhs.recovery_only == rhs.recovery_only &&
+ lhs.verify == rhs.verify &&
+ lhs.force_crypt == rhs.force_crypt &&
+ lhs.no_emulated_sd == rhs.no_emulated_sd &&
+ lhs.no_trim == rhs.no_trim &&
+ lhs.file_encryption == rhs.file_encryption &&
+ lhs.formattable == rhs.formattable &&
+ lhs.slot_select == rhs.slot_select &&
+ lhs.force_fde_or_fbe == rhs.force_fde_or_fbe &&
+ lhs.late_mount == rhs.late_mount &&
+ lhs.no_fail == rhs.no_fail &&
+ lhs.verify_at_boot == rhs.verify_at_boot &&
+ lhs.quota == rhs.quota &&
+ lhs.avb == rhs.avb &&
+ lhs.logical == rhs.logical &&
+ lhs.checkpoint_blk == rhs.checkpoint_blk &&
+ lhs.checkpoint_fs == rhs.checkpoint_fs &&
+ lhs.first_stage_mount == rhs.first_stage_mount &&
+ lhs.slot_select_other == rhs.slot_select_other &&
+ lhs.fs_verity == rhs.fs_verity;
+ // clang-format on
+}
+
+// TODO(124837435): enable it later when it can pass TreeHugger.
+TEST(fs_mgr, DISABLED_ReadFstabFromFile_FsMgrFlags) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults wait,check,nonremovable,recoveryonly,verifyatboot,verify
+source none1 swap defaults avb,noemulatedsd,notrim,formattable,slotselect,nofail
+source none2 swap defaults first_stage_mount,latemount,quota,logical,slotselect_other
+source none3 swap defaults checkpoint=block
+source none4 swap defaults checkpoint=fs
+source none5 swap defaults defaults
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(6U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.wait = true;
+ flags.check = true;
+ flags.nonremovable = true;
+ flags.recovery_only = true;
+ flags.verify_at_boot = true;
+ flags.verify = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.avb = true;
+ flags.no_emulated_sd = true;
+ flags.no_trim = true;
+ flags.formattable = true;
+ flags.slot_select = true;
+ flags.no_fail = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.first_stage_mount = true;
+ flags.late_mount = true;
+ flags.quota = true;
+ flags.logical = true;
+ flags.slot_select_other = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.checkpoint_blk = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ entry++;
+
+ EXPECT_EQ("none4", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.checkpoint_fs = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ entry++;
+
+ EXPECT_EQ("none5", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_AllBad) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_loopback_path,zram_loopback_size,zram_backing_dev_path
+
+source none1 swap defaults encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
+
+source none2 swap defaults forcefdeorfbe=
+
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(3U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ EXPECT_EQ("", entry->key_loc);
+ EXPECT_EQ("", entry->key_dir);
+ EXPECT_EQ(0, entry->length);
+ EXPECT_EQ("", entry->label);
+ EXPECT_EQ(-1, entry->partnum);
+ EXPECT_EQ(-1, entry->swap_prio);
+ EXPECT_EQ(0, entry->max_comp_streams);
+ EXPECT_EQ(0, entry->zram_size);
+ EXPECT_EQ(0, entry->reserved_size);
+ EXPECT_EQ("", entry->file_contents_mode);
+ EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ(0, entry->erase_blk_size);
+ EXPECT_EQ(0, entry->logical_blk_size);
+ EXPECT_EQ("", entry->sysfs_path);
+ EXPECT_EQ("", entry->zram_loopback_path);
+ EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+ EXPECT_EQ("", entry->zram_backing_dev_path);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.crypt = true;
+ flags.force_crypt = true;
+ flags.file_encryption = true;
+ flags.avb = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ EXPECT_EQ("", entry->key_loc);
+ EXPECT_EQ("", entry->key_dir);
+ EXPECT_EQ(0, entry->length);
+ EXPECT_EQ("", entry->label);
+ EXPECT_EQ(-1, entry->partnum);
+ EXPECT_EQ(-1, entry->swap_prio);
+ EXPECT_EQ(0, entry->max_comp_streams);
+ EXPECT_EQ(0, entry->zram_size);
+ EXPECT_EQ(0, entry->reserved_size);
+ EXPECT_EQ("", entry->file_contents_mode);
+ EXPECT_EQ("", entry->file_names_mode);
+ EXPECT_EQ(0, entry->erase_blk_size);
+ EXPECT_EQ(0, entry->logical_blk_size);
+ EXPECT_EQ("", entry->sysfs_path);
+ EXPECT_EQ("", entry->zram_loopback_path);
+ EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+ EXPECT_EQ("", entry->zram_backing_dev_path);
+ entry++;
+
+ // forcefdeorfbe sets file_contents_mode and file_names_mode by default, so test it separately.
+ EXPECT_EQ("none2", entry->mount_point);
+ {
+ FstabEntry::FsMgrFlags flags = {};
+ flags.force_fde_or_fbe = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ }
+ EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+ EXPECT_EQ("", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Encryptable) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults encryptable=/dir/key
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.crypt = true;
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("/dir/key", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_VoldManaged) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults voldmanaged=:
+source none1 swap defaults voldmanaged=sdcard
+source none2 swap defaults voldmanaged=sdcard:3
+source none3 swap defaults voldmanaged=sdcard:auto
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(4U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.vold_managed = true;
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_TRUE(entry->label.empty());
+ EXPECT_EQ(-1, entry->partnum);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_TRUE(entry->label.empty());
+ EXPECT_EQ(-1, entry->partnum);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("sdcard", entry->label);
+ EXPECT_EQ(3, entry->partnum);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("sdcard", entry->label);
+ EXPECT_EQ(-1, entry->partnum);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Length) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults length=blah
+source none1 swap defaults length=123456
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(2U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->length);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(123456, entry->length);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Swapprio) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults swapprio=blah
+source none1 swap defaults swapprio=123456
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(2U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(-1, entry->swap_prio);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(123456, entry->swap_prio);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ZramSize) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults zramsize=blah
+source none1 swap defaults zramsize=123456
+source none2 swap defaults zramsize=blah%
+source none3 swap defaults zramsize=5%
+source none4 swap defaults zramsize=105%
+source none5 swap defaults zramsize=%
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(6U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->zram_size);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(123456, entry->zram_size);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->zram_size);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_NE(0, entry->zram_size);
+ entry++;
+
+ EXPECT_EQ("none4", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->zram_size);
+ entry++;
+
+ EXPECT_EQ("none5", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->zram_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults forceencrypt=/dir/key
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.force_crypt = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+ EXPECT_EQ("/dir/key", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceFdeOrFbe) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults forcefdeorfbe=/dir/key
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.force_fde_or_fbe = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+ EXPECT_EQ("/dir/key", entry->key_loc);
+ EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults fileencryption=blah
+source none1 swap defaults fileencryption=software
+source none2 swap defaults fileencryption=aes-256-xts
+source none3 swap defaults fileencryption=adiantum
+source none4 swap defaults fileencryption=adiantum:aes-256-heh
+source none5 swap defaults fileencryption=ice
+source none6 swap defaults fileencryption=ice:blah
+source none7 swap defaults fileencryption=ice:aes-256-cts
+source none8 swap defaults fileencryption=ice:aes-256-heh
+source none9 swap defaults fileencryption=ice:adiantum
+source none10 swap defaults fileencryption=ice:adiantum:
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(11U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.file_encryption = true;
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("", entry->file_contents_mode);
+ EXPECT_EQ("", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("adiantum", entry->file_contents_mode);
+ EXPECT_EQ("adiantum", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none4", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("adiantum", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-heh", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none5", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("ice", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none6", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("ice", entry->file_contents_mode);
+ EXPECT_EQ("", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none7", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("ice", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none8", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("ice", entry->file_contents_mode);
+ EXPECT_EQ("aes-256-heh", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none9", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("ice", entry->file_contents_mode);
+ EXPECT_EQ("adiantum", entry->file_names_mode);
+
+ entry++;
+ EXPECT_EQ("none10", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ("", entry->file_contents_mode);
+ EXPECT_EQ("", entry->file_names_mode);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults max_comp_streams=blah
+source none1 swap defaults max_comp_streams=123456
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(2U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->max_comp_streams);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(123456, entry->max_comp_streams);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ReservedSize) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults reservedsize=blah
+source none1 swap defaults reservedsize=2
+source none2 swap defaults reservedsize=1K
+source none3 swap defaults reservedsize=2m
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(4U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->reserved_size);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(2, entry->reserved_size);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(1024, entry->reserved_size);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(2 * 1024 * 1024, entry->reserved_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_EraseBlk) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults eraseblk=blah
+source none1 swap defaults eraseblk=4000
+source none2 swap defaults eraseblk=5000
+source none3 swap defaults eraseblk=8192
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(4U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->erase_blk_size);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->erase_blk_size);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->erase_blk_size);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(8192, entry->erase_blk_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Logicalblk) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults logicalblk=blah
+source none1 swap defaults logicalblk=4000
+source none2 swap defaults logicalblk=5000
+source none3 swap defaults logicalblk=8192
+)fs";
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(4U, fstab.size());
+
+ FstabEntry::FsMgrFlags flags = {};
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->logical_blk_size);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->logical_blk_size);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(0, entry->logical_blk_size);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+ EXPECT_EQ(8192, entry->logical_blk_size);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Avb) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults avb=vbmeta_partition
+source none1 swap defaults avb_keys=/path/to/test.avbpubkey
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(2U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+
+ FstabEntry::FsMgrFlags flags = {};
+ flags.avb = true;
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+ EXPECT_EQ("vbmeta_partition", entry->vbmeta_partition);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ FstabEntry::FsMgrFlags empty_flags = {}; // no flags should be set for avb_keys.
+ EXPECT_TRUE(CompareFlags(empty_flags, entry->fs_mgr_flags));
+ EXPECT_EQ("/path/to/test.avbpubkey", entry->avb_keys);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_KeyDirectory) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults keydirectory=/dir/key
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+
+ FstabEntry::FsMgrFlags flags = {};
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+ EXPECT_EQ("/dir/key", entry->key_dir);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults sysfs_path=/sys/device
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(1U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+
+ FstabEntry::FsMgrFlags flags = {};
+ EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+ EXPECT_EQ("/sys/device", entry->sysfs_path);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Zram) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+source none0 swap defaults zram_loopback_path=/dev/path
+
+source none1 swap defaults zram_loopback_size=blah
+source none2 swap defaults zram_loopback_size=2
+source none3 swap defaults zram_loopback_size=1K
+source none4 swap defaults zram_loopback_size=2m
+
+source none5 swap defaults zram_backing_dev_path=/dev/path2
+
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ ASSERT_EQ(6U, fstab.size());
+
+ auto entry = fstab.begin();
+ EXPECT_EQ("none0", entry->mount_point);
+ EXPECT_EQ("/dev/path", entry->zram_loopback_path);
+ entry++;
+
+ EXPECT_EQ("none1", entry->mount_point);
+ EXPECT_EQ(512U * 1024U * 1024U, entry->zram_loopback_size);
+ entry++;
+
+ EXPECT_EQ("none2", entry->mount_point);
+ EXPECT_EQ(2U, entry->zram_loopback_size);
+ entry++;
+
+ EXPECT_EQ("none3", entry->mount_point);
+ EXPECT_EQ(1024U, entry->zram_loopback_size);
+ entry++;
+
+ EXPECT_EQ("none4", entry->mount_point);
+ EXPECT_EQ(2U * 1024U * 1024U, entry->zram_loopback_size);
+ entry++;
+
+ EXPECT_EQ("none5", entry->mount_point);
+ EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
}
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
new file mode 100644
index 0000000..f08cab2
--- /dev/null
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.vendoroverlay;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the vendor overlay feature. Requires adb remount with OverlayFS.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class VendorOverlayHostTest extends BaseHostJUnit4Test {
+ boolean wasRoot = false;
+
+ @Before
+ public void setup() throws DeviceNotAvailableException {
+ wasRoot = getDevice().isAdbRoot();
+ if (!wasRoot) {
+ Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
+ }
+
+ Assume.assumeTrue("Skipping vendor overlay test due to lack of necessary OverlayFS support",
+ testConditionsMet());
+
+ getDevice().remountSystemWritable();
+ // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.
+ Pattern vendorPattern = Pattern.compile("^overlay .+ /vendor$", Pattern.MULTILINE);
+ Pattern productPattern = Pattern.compile("^overlay .+ /product$", Pattern.MULTILINE);
+ CommandResult result = getDevice().executeShellV2Command("df");
+ Assume.assumeTrue("OverlayFS not used for adb remount on /vendor",
+ vendorPattern.matcher(result.getStdout()).find());
+ Assume.assumeTrue("OverlayFS not used for adb remount on /product",
+ productPattern.matcher(result.getStdout()).find());
+ }
+
+ private boolean cmdSucceeded(CommandResult result) {
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
+ private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {
+ CommandResult result = getDevice().executeShellV2Command("mkdir -p " + dir);
+ Assume.assumeTrue("Couldn't create " + dir, cmdSucceeded(result));
+ }
+
+ /**
+ * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.
+ */
+ @Test
+ public void testVendorOverlay() throws DeviceNotAvailableException {
+ String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+
+ // Create files and modify policy
+ CommandResult result = getDevice().executeShellV2Command(
+ "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
+ "/.* u:object_r:vendor_file:s0'" + " >> /system/etc/selinux/plat_file_contexts");
+ Assume.assumeTrue("Couldn't modify plat_file_contexts", cmdSucceeded(result));
+ assumeMkdirSuccess("/vendor/testdir");
+ assumeMkdirSuccess("/vendor/diffcontext");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ result = getDevice().executeShellV2Command(
+ "echo overlay > /product/vendor_overlay/'" + vndkVersion + "'/testdir/test");
+ Assume.assumeTrue("Couldn't create text file in testdir", cmdSucceeded(result));
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/noexist/test");
+ assumeMkdirSuccess("/product/vendor_overlay/'" + vndkVersion + "'/diffcontext/test");
+ result = getDevice().executeShellV2Command(
+ "restorecon -r /product/vendor_overlay/'" + vndkVersion + "'/testdir");
+ Assume.assumeTrue("Couldn't write testdir context", cmdSucceeded(result));
+
+ getDevice().reboot();
+
+ // Test that the file was overlaid properly
+ result = getDevice().executeShellV2Command("[ $(cat /vendor/testdir/test) = overlay ]");
+ Assert.assertTrue("test file was not overlaid onto /vendor/", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/noexist/test ]");
+ Assert.assertTrue("noexist dir shouldn't exist on /vendor", cmdSucceeded(result));
+ result = getDevice().executeShellV2Command("[ ! -d /vendor/diffcontext/test ]");
+ Assert.assertTrue("diffcontext dir shouldn't exist on /vendor", cmdSucceeded(result));
+ }
+
+ // Duplicate of fs_mgr_overlayfs_valid() logic
+ // Requires root
+ public boolean testConditionsMet() throws DeviceNotAvailableException {
+ if (cmdSucceeded(getDevice().executeShellV2Command(
+ "[ -e /sys/module/overlay/parameters/override_creds ]"))) {
+ return true;
+ }
+ if (cmdSucceeded(getDevice().executeShellV2Command("[ ! -e /sys/module/overlay ]"))) {
+ return false;
+ }
+ CommandResult result = getDevice().executeShellV2Command("awk '{ print $3 }' /proc/version");
+ Pattern kernelVersionPattern = Pattern.compile("([1-9])[.]([0-9]+).*");
+ Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());
+ kernelVersionMatcher.find();
+ int majorKernelVersion;
+ int minorKernelVersion;
+ try {
+ majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));
+ minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));
+ } catch (Exception e) {
+ return false;
+ }
+ if (majorKernelVersion < 4) {
+ return true;
+ }
+ if (majorKernelVersion > 4) {
+ return false;
+ }
+ if (minorKernelVersion > 6) {
+ return false;
+ }
+ return true;
+ }
+
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ if (getDevice().executeAdbCommand("enable-verity").contains("Now reboot your device")) {
+ getDevice().reboot();
+ }
+ if (!wasRoot) {
+ getDevice().disableAdbRoot();
+ }
+ }
+}
+
diff --git a/fs_mgr/tests/vendor-overlay-test.xml b/fs_mgr/tests/vendor-overlay-test.xml
new file mode 100644
index 0000000..0b5c8cc
--- /dev/null
+++ b/fs_mgr/tests/vendor-overlay-test.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for vendor overlay test cases">
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="fs_mgr_vendor_overlay_test.jar" />
+ </test>
+</configuration>
+
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index f78093b..4fb0b83 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -27,6 +27,7 @@
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
+#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
@@ -36,26 +37,27 @@
#include <string>
#include <vector>
-using DeviceMapper = ::android::dm::DeviceMapper;
-using DmTable = ::android::dm::DmTable;
-using DmTarget = ::android::dm::DmTarget;
-using DmTargetLinear = ::android::dm::DmTargetLinear;
-using DmTargetZero = ::android::dm::DmTargetZero;
-using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
-using DmTargetBow = ::android::dm::DmTargetBow;
-using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
+using namespace std::literals::string_literals;
+using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
static int Usage(void) {
std::cerr << "usage: dmctl <command> [command options]" << std::endl;
+ std::cerr << " dmctl -f file" << std::endl;
std::cerr << "commands:" << std::endl;
std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
std::cerr << " delete <dm-name>" << std::endl;
- std::cerr << " list <devices | targets>" << std::endl;
+ std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " info <dm-name>" << std::endl;
+ std::cerr << " status <dm-name>" << std::endl;
+ std::cerr << " resume <dm-name>" << std::endl;
+ std::cerr << " suspend <dm-name>" << std::endl;
std::cerr << " table <dm-name>" << std::endl;
std::cerr << " help" << std::endl;
std::cerr << std::endl;
+ std::cerr << "-f file reads command and all parameters from named file" << std::endl;
+ std::cerr << std::endl;
std::cerr << "Target syntax:" << std::endl;
std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
return -EINVAL;
@@ -116,6 +118,62 @@
}
std::string block_device = NextArg();
return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
+ } else if (target_type == "snapshot-origin") {
+ if (!HasArgs(1)) {
+ std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
+ return nullptr;
+ }
+ std::string block_device = NextArg();
+ return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
+ block_device);
+ } else if (target_type == "snapshot") {
+ if (!HasArgs(4)) {
+ std::cerr
+ << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string mode_str = NextArg();
+ std::string chunk_size_str = NextArg();
+
+ SnapshotStorageMode mode;
+ if (mode_str == "P") {
+ mode = SnapshotStorageMode::Persistent;
+ } else if (mode_str == "N") {
+ mode = SnapshotStorageMode::Transient;
+ } else {
+ std::cerr << "Unrecognized mode: " << mode_str << "\n";
+ return nullptr;
+ }
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
+ } else if (target_type == "snapshot-merge") {
+ if (!HasArgs(3)) {
+ std::cerr
+ << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string base_device = NextArg();
+ std::string cow_device = NextArg();
+ std::string chunk_size_str = NextArg();
+ SnapshotStorageMode mode = SnapshotStorageMode::Merge;
+
+ uint32_t chunk_size;
+ if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
+ std::cerr << "Chunk size must be an unsigned integer.\n";
+ return nullptr;
+ }
+ return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
+ cow_device, mode, chunk_size);
} else {
std::cerr << "Unrecognized target type: " << target_type << std::endl;
return nullptr;
@@ -197,7 +255,8 @@
return 0;
}
-static int DmListTargets(DeviceMapper& dm) {
+static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
+ [[maybe_unused]] char** argv) {
std::vector<DmTargetTypeInfo> targets;
if (!dm.GetAvailableTargets(&targets)) {
std::cerr << "Failed to read available device mapper targets" << std::endl;
@@ -218,7 +277,7 @@
return 0;
}
-static int DmListDevices(DeviceMapper& dm) {
+static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
std::vector<DmBlockDevice> devices;
if (!dm.GetAvailableDevices(&devices)) {
std::cerr << "Failed to read available device mapper devices" << std::endl;
@@ -230,15 +289,37 @@
return 0;
}
+ bool verbose = (argc && (argv[0] == "-v"s));
for (const auto& dev : devices) {
std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
<< dev.Minor() << std::endl;
+ if (verbose) {
+ std::vector<DeviceMapper::TargetInfo> table;
+ if (!dm.GetTableInfo(dev.name(), &table)) {
+ std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+
+ uint32_t target_num = 1;
+ for (const auto& target : table) {
+ std::cout << " target#" << target_num << ": ";
+ std::cout << target.spec.sector_start << "-"
+ << (target.spec.sector_start + target.spec.length) << ": "
+ << target.spec.target_type;
+ if (!target.data.empty()) {
+ std::cout << ", " << target.data;
+ }
+ std::cout << std::endl;
+ target_num++;
+ }
+ }
}
return 0;
}
-static const std::map<std::string, std::function<int(DeviceMapper&)>> listmap = {
+static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
{"targets", DmListTargets},
{"devices", DmListDevices},
};
@@ -251,7 +332,7 @@
DeviceMapper& dm = DeviceMapper::Instance();
for (const auto& l : listmap) {
- if (l.first == argv[0]) return l.second(dm);
+ if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
}
std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
@@ -279,7 +360,42 @@
return 0;
}
-static int TableCmdHandler(int argc, char** argv) {
+static int InfoCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ auto info = dm.GetDetailedInfo(argv[0]);
+ if (!info) {
+ std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+
+ constexpr int spacing = 14;
+ std::cout << std::left << std::setw(spacing) << "device"
+ << ": " << argv[0] << std::endl;
+ std::cout << std::left << std::setw(spacing) << "active"
+ << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
+ std::cout << std::left << std::setw(spacing) << "access"
+ << ": ";
+ if (info->IsReadOnly()) {
+ std::cout << "ro ";
+ } else {
+ std::cout << "rw ";
+ }
+ std::cout << std::endl;
+ std::cout << std::left << std::setw(spacing) << "activeTable"
+ << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
+ std::cout << std::left << std::setw(spacing) << "inactiveTable"
+ << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
+ std::cout << std::left << std::setw(spacing) << "bufferFull"
+ << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
+ return 0;
+}
+
+static int DumpTable(const std::string& mode, int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
return -EINVAL;
@@ -287,9 +403,18 @@
DeviceMapper& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
- if (!dm.GetTableStatus(argv[0], &table)) {
- std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
- return -EINVAL;
+ if (mode == "status") {
+ if (!dm.GetTableStatus(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "table") {
+ if (!dm.GetTableInfo(argv[0], &table)) {
+ std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
+ << std::endl;
+ return -EINVAL;
+ }
}
std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
for (const auto& target : table) {
@@ -304,6 +429,42 @@
return 0;
}
+static int TableCmdHandler(int argc, char** argv) {
+ return DumpTable("table", argc, argv);
+}
+
+static int StatusCmdHandler(int argc, char** argv) {
+ return DumpTable("status", argc, argv);
+}
+
+static int ResumeCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
+ std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int SuspendCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
+ std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ return 0;
+}
+
static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
// clang-format off
{"create", DmCreateCmdHandler},
@@ -311,16 +472,48 @@
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
+ {"info", InfoCmdHandler},
{"table", TableCmdHandler},
+ {"status", StatusCmdHandler},
+ {"resume", ResumeCmdHandler},
+ {"suspend", SuspendCmdHandler},
// clang-format on
};
+static bool ReadFile(const char* filename, std::vector<std::string>* args,
+ std::vector<char*>* arg_ptrs) {
+ std::ifstream file(filename);
+ if (!file) return false;
+
+ std::string arg;
+ while (file >> arg) args->push_back(arg);
+
+ for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
+ return true;
+}
+
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
if (argc < 2) {
return Usage();
}
+ std::vector<std::string> args;
+ std::vector<char*> arg_ptrs;
+ if (std::string("-f") == argv[1]) {
+ if (argc != 3) {
+ return Usage();
+ }
+
+ args.push_back(argv[0]);
+ if (!ReadFile(argv[2], &args, &arg_ptrs)) {
+ return Usage();
+ }
+
+ argc = arg_ptrs.size();
+ argv = &arg_ptrs[0];
+ }
+
for (const auto& cmd : cmdmap) {
if (cmd.first == argv[1]) {
return cmd.second(argc - 2, argv + 2);
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
new file mode 100644
index 0000000..778e08c
--- /dev/null
+++ b/gatekeeperd/Android.bp
@@ -0,0 +1,84 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "gatekeeperd",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wunused",
+ ],
+ srcs: [
+ "gatekeeperd.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libgatekeeper",
+ "libgsi",
+ "liblog",
+ "libhardware",
+ "libbase",
+ "libutils",
+ "libcrypto",
+ "libkeystore_aidl",
+ "libkeystore_binder",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "android.hardware.gatekeeper@1.0",
+ "libgatekeeper_aidl",
+ ],
+
+ static_libs: ["libscrypt_static"],
+ include_dirs: ["external/scrypt/lib/crypto"],
+ init_rc: ["gatekeeperd.rc"],
+}
+
+filegroup {
+ name: "gatekeeper_aidl",
+ srcs: [
+ "binder/android/service/gatekeeper/IGateKeeperService.aidl",
+ ],
+ path: "binder",
+}
+
+cc_library_shared {
+ name: "libgatekeeper_aidl",
+ srcs: [
+ ":gatekeeper_aidl",
+ "GateKeeperResponse.cpp",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ include_dirs: [
+ "system/core/gatekeeperd/binder",
+ "frameworks/base/core/java/",
+ ],
+ },
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ export_shared_lib_headers: [
+ "libbinder",
+ ],
+}
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
deleted file mode 100644
index 6d5d1ea..0000000
--- a/gatekeeperd/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
-LOCAL_SRC_FILES := \
- SoftGateKeeperDevice.cpp \
- IGateKeeperService.cpp \
- gatekeeperd.cpp
-
-LOCAL_MODULE := gatekeeperd
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libgatekeeper \
- liblog \
- libhardware \
- libbase \
- libutils \
- libcrypto \
- libkeystore_aidl \
- libkeystore_binder \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- android.hardware.gatekeeper@1.0 \
-
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_INIT_RC := gatekeeperd.rc
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/gatekeeperd/GateKeeperResponse.cpp b/gatekeeperd/GateKeeperResponse.cpp
new file mode 100644
index 0000000..ca0c98f
--- /dev/null
+++ b/gatekeeperd/GateKeeperResponse.cpp
@@ -0,0 +1,83 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "gatekeeperd"
+
+#include <gatekeeper/GateKeeperResponse.h>
+
+#include <binder/Parcel.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+status_t GateKeeperResponse::readFromParcel(const Parcel* in) {
+ if (in == nullptr) {
+ LOG(ERROR) << "readFromParcel got null in parameter";
+ return BAD_VALUE;
+ }
+ timeout_ = 0;
+ should_reenroll_ = false;
+ payload_ = {};
+ response_code_ = ResponseCode(in->readInt32());
+ if (response_code_ == ResponseCode::OK) {
+ should_reenroll_ = in->readInt32();
+ ssize_t length = in->readInt32();
+ if (length > 0) {
+ length = in->readInt32();
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(in->readInplace(length));
+ if (buf == nullptr) {
+ LOG(ERROR) << "readInplace returned null buffer for length " << length;
+ return BAD_VALUE;
+ }
+ payload_.resize(length);
+ std::copy(buf, buf + length, payload_.data());
+ }
+ } else if (response_code_ == ResponseCode::RETRY) {
+ timeout_ = in->readInt32();
+ }
+ return NO_ERROR;
+}
+status_t GateKeeperResponse::writeToParcel(Parcel* out) const {
+ if (out == nullptr) {
+ LOG(ERROR) << "writeToParcel got null out parameter";
+ return BAD_VALUE;
+ }
+ out->writeInt32(int32_t(response_code_));
+ if (response_code_ == ResponseCode::OK) {
+ out->writeInt32(should_reenroll_);
+ out->writeInt32(payload_.size());
+ if (payload_.size() != 0) {
+ out->writeInt32(payload_.size());
+ uint8_t* buf = reinterpret_cast<uint8_t*>(out->writeInplace(payload_.size()));
+ if (buf == nullptr) {
+ LOG(ERROR) << "writeInplace returned null buffer for length " << payload_.size();
+ return BAD_VALUE;
+ }
+ std::copy(payload_.begin(), payload_.end(), buf);
+ }
+ } else if (response_code_ == ResponseCode::RETRY) {
+ out->writeInt32(timeout_);
+ }
+ return NO_ERROR;
+}
+
+} // namespace gatekeeper
+} // namespace service
+} // namespace android
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
deleted file mode 100644
index 43d5708..0000000
--- a/gatekeeperd/IGateKeeperService.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-#define LOG_TAG "GateKeeperService"
-#include <utils/Log.h>
-
-#include "IGateKeeperService.h"
-
-namespace android {
-
-const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService");
-const android::String16& IGateKeeperService::getInterfaceDescriptor() const {
- return IGateKeeperService::descriptor;
-}
-
-status_t BnGateKeeperService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case ENROLL: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- uint32_t uid = data.readInt32();
-
- ssize_t currentPasswordHandleSize = data.readInt32();
- const uint8_t *currentPasswordHandle =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
- if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
- ssize_t currentPasswordSize = data.readInt32();
- const uint8_t *currentPassword =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
- if (!currentPassword) currentPasswordSize = 0;
-
- ssize_t desiredPasswordSize = data.readInt32();
- const uint8_t *desiredPassword =
- static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize));
- if (!desiredPassword) desiredPasswordSize = 0;
-
- uint8_t *out = NULL;
- uint32_t outSize = 0;
- int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize,
- currentPassword, currentPasswordSize, desiredPassword,
- desiredPasswordSize, &out, &outSize);
-
- reply->writeNoException();
- reply->writeInt32(1);
- if (ret == 0 && outSize > 0 && out != NULL) {
- reply->writeInt32(GATEKEEPER_RESPONSE_OK);
- reply->writeInt32(0);
- reply->writeInt32(outSize);
- reply->writeInt32(outSize);
- void *buf = reply->writeInplace(outSize);
- memcpy(buf, out, outSize);
- delete[] out;
- } else if (ret > 0) {
- reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
- reply->writeInt32(ret);
- } else {
- reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
- }
- return OK;
- }
- case VERIFY: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- uint32_t uid = data.readInt32();
- ssize_t currentPasswordHandleSize = data.readInt32();
- const uint8_t *currentPasswordHandle =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
- if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
- ssize_t currentPasswordSize = data.readInt32();
- const uint8_t *currentPassword =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
- if (!currentPassword) currentPasswordSize = 0;
-
- bool request_reenroll = false;
- int ret = verify(uid, (uint8_t *) currentPasswordHandle,
- currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
- &request_reenroll);
-
- reply->writeNoException();
- reply->writeInt32(1);
- if (ret == 0) {
- reply->writeInt32(GATEKEEPER_RESPONSE_OK);
- reply->writeInt32(request_reenroll ? 1 : 0);
- reply->writeInt32(0); // no payload returned from this call
- } else if (ret > 0) {
- reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
- reply->writeInt32(ret);
- } else {
- reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
- }
- return OK;
- }
- case VERIFY_CHALLENGE: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- uint32_t uid = data.readInt32();
- uint64_t challenge = data.readInt64();
- ssize_t currentPasswordHandleSize = data.readInt32();
- const uint8_t *currentPasswordHandle =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
- if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
- ssize_t currentPasswordSize = data.readInt32();
- const uint8_t *currentPassword =
- static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
- if (!currentPassword) currentPasswordSize = 0;
-
-
- uint8_t *out = NULL;
- uint32_t outSize = 0;
- bool request_reenroll = false;
- int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle,
- currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
- &out, &outSize, &request_reenroll);
- reply->writeNoException();
- reply->writeInt32(1);
- if (ret == 0 && outSize > 0 && out != NULL) {
- reply->writeInt32(GATEKEEPER_RESPONSE_OK);
- reply->writeInt32(request_reenroll ? 1 : 0);
- reply->writeInt32(outSize);
- reply->writeInt32(outSize);
- void *buf = reply->writeInplace(outSize);
- memcpy(buf, out, outSize);
- delete[] out;
- } else if (ret > 0) {
- reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
- reply->writeInt32(ret);
- } else {
- reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
- }
- return OK;
- }
- case GET_SECURE_USER_ID: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- uint32_t uid = data.readInt32();
- uint64_t sid = getSecureUserId(uid);
- reply->writeNoException();
- reply->writeInt64(sid);
- return OK;
- }
- case CLEAR_SECURE_USER_ID: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- uint32_t uid = data.readInt32();
- clearSecureUserId(uid);
- reply->writeNoException();
- return OK;
- }
- case REPORT_DEVICE_SETUP_COMPLETE: {
- CHECK_INTERFACE(IGateKeeperService, data, reply);
- reportDeviceSetupComplete();
- reply->writeNoException();
- return OK;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-};
-
-
-}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
deleted file mode 100644
index 2816efc..0000000
--- a/gatekeeperd/IGateKeeperService.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef IGATEKEEPER_SERVICE_H_
-#define IGATEKEEPER_SERVICE_H_
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/*
- * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl
- */
-class IGateKeeperService : public IInterface {
-public:
- enum {
- ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0,
- VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1,
- VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2,
- GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3,
- CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4,
- REPORT_DEVICE_SETUP_COMPLETE = IBinder::FIRST_CALL_TRANSACTION + 5,
- };
-
- enum {
- GATEKEEPER_RESPONSE_OK = 0,
- GATEKEEPER_RESPONSE_RETRY = 1,
- GATEKEEPER_RESPONSE_ERROR = -1,
- };
-
- // DECLARE_META_INTERFACE - C++ client interface not needed
- static const android::String16 descriptor;
- virtual const android::String16& getInterfaceDescriptor() const;
- IGateKeeperService() {}
- virtual ~IGateKeeperService() {}
-
- /**
- * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure.
- * Returns:
- * - 0 on success
- * - A timestamp T > 0 if the call has failed due to throttling and should not
- * be reattempted until T milliseconds have elapsed
- * - -1 on failure
- */
- virtual int enroll(uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0;
-
- /**
- * Verifies a password previously enrolled with the GateKeeper.
- * Returns:
- * - 0 on success
- * - A timestamp T > 0 if the call has failed due to throttling and should not
- * be reattempted until T milliseconds have elapsed
- * - -1 on failure
- */
- virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle,
- uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- bool *request_reenroll) = 0;
-
- /**
- * Verifies a password previously enrolled with the GateKeeper.
- * Returns:
- * - 0 on success
- * - A timestamp T > 0 if the call has failed due to throttling and should not
- * be reattempted until T milliseconds have elapsed
- * - -1 on failure
- */
- virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0;
- /**
- * Returns the secure user ID for the provided android user
- */
- virtual uint64_t getSecureUserId(uint32_t uid) = 0;
-
- /**
- * Clears the secure user ID associated with the user.
- */
- virtual void clearSecureUserId(uint32_t uid) = 0;
-
- /**
- * Notifies gatekeeper that device setup has been completed and any potentially still existing
- * state from before a factory reset can be cleaned up (if it has not been already).
- */
- virtual void reportDeviceSetupComplete() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnGateKeeperService: public BnInterface<IGateKeeperService> {
-public:
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif
-
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
deleted file mode 100644
index 2f4f4d7..0000000
--- a/gatekeeperd/SoftGateKeeper.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef SOFT_GATEKEEPER_H_
-#define SOFT_GATEKEEPER_H_
-
-extern "C" {
-#include <openssl/rand.h>
-#include <openssl/sha.h>
-
-#include <crypto_scrypt.h>
-}
-
-#include <android-base/memory.h>
-#include <gatekeeper/gatekeeper.h>
-
-#include <iostream>
-#include <unordered_map>
-#include <memory>
-
-namespace gatekeeper {
-
-struct fast_hash_t {
- uint64_t salt;
- uint8_t digest[SHA256_DIGEST_LENGTH];
-};
-
-class SoftGateKeeper : public GateKeeper {
-public:
- static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
-
- // scrypt params
- static const uint64_t N = 16384;
- static const uint32_t r = 8;
- static const uint32_t p = 1;
-
- static const int MAX_UINT_32_CHARS = 11;
-
- SoftGateKeeper() {
- key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
- memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
- }
-
- virtual ~SoftGateKeeper() {
- }
-
- virtual bool GetAuthTokenKey(const uint8_t **auth_token_key,
- uint32_t *length) const {
- if (auth_token_key == NULL || length == NULL) return false;
- uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
- memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
- *auth_token_key = auth_token_key_copy;
- *length = SIGNATURE_LENGTH_BYTES;
- return true;
- }
-
- virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) {
- if (password_key == NULL || length == NULL) return;
- uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
- memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
- *password_key = password_key_copy;
- *length = SIGNATURE_LENGTH_BYTES;
- }
-
- virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length,
- const uint8_t *, uint32_t, const uint8_t *password,
- uint32_t password_length, salt_t salt) const {
- if (signature == NULL) return;
- crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt),
- sizeof(salt), N, r, p, signature, signature_length);
- }
-
- virtual void GetRandom(void *random, uint32_t requested_length) const {
- if (random == NULL) return;
- RAND_pseudo_bytes((uint8_t *) random, requested_length);
- }
-
- virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length,
- const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const {
- if (signature == NULL) return;
- memset(signature, 0, signature_length);
- }
-
- virtual uint64_t GetMillisecondsSinceBoot() const {
- struct timespec time;
- int res = clock_gettime(CLOCK_BOOTTIME, &time);
- if (res < 0) return 0;
- return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
- }
-
- virtual bool IsHardwareBacked() const {
- return false;
- }
-
- virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
- bool /* secure */) {
- failure_record_t *stored = &failure_map_[uid];
- if (user_id != stored->secure_user_id) {
- stored->secure_user_id = user_id;
- stored->last_checked_timestamp = 0;
- stored->failure_counter = 0;
- }
- memcpy(record, stored, sizeof(*record));
- return true;
- }
-
- virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
- failure_record_t *stored = &failure_map_[uid];
- stored->secure_user_id = user_id;
- stored->last_checked_timestamp = 0;
- stored->failure_counter = 0;
- return true;
- }
-
- virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) {
- failure_map_[uid] = *record;
- return true;
- }
-
- fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) {
- fast_hash_t fast_hash;
- size_t digest_size = password.length + sizeof(salt);
- std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
- memcpy(digest.get(), &salt, sizeof(salt));
- memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length);
-
- SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest);
-
- fast_hash.salt = salt;
- return fast_hash;
- }
-
- bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) {
- fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
- return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
- }
-
- bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
- uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
- FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
- if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
- return true;
- } else {
- if (GateKeeper::DoVerify(expected_handle, password)) {
- uint64_t salt;
- GetRandom(&salt, sizeof(salt));
- fast_hash_map_[user_id] = ComputeFastHash(password, salt);
- return true;
- }
- }
-
- return false;
- }
-
-private:
-
- typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
- typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
-
- std::unique_ptr<uint8_t[]> key_;
- FailureRecordMap failure_map_;
- FastHashMap fast_hash_map_;
-};
-}
-
-#endif // SOFT_GATEKEEPER_H_
diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp
deleted file mode 100644
index f5e2ce6..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "SoftGateKeeper.h"
-#include "SoftGateKeeperDevice.h"
-
-namespace android {
-
-int SoftGateKeeperDevice::enroll(uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
- if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
- desired_password == NULL || desired_password_length == 0)
- return -EINVAL;
-
- // Current password and current password handle go together
- if (current_password_handle == NULL || current_password_handle_length == 0 ||
- current_password == NULL || current_password_length == 0) {
- current_password_handle = NULL;
- current_password_handle_length = 0;
- current_password = NULL;
- current_password_length = 0;
- }
-
- SizedBuffer desired_password_buffer(desired_password_length);
- memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
- SizedBuffer current_password_handle_buffer(current_password_handle_length);
- if (current_password_handle) {
- memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
- current_password_handle_length);
- }
-
- SizedBuffer current_password_buffer(current_password_length);
- if (current_password) {
- memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
- }
-
- EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
- ¤t_password_buffer);
- EnrollResponse response;
-
- impl_->Enroll(request, &response);
-
- if (response.error == ERROR_RETRY) {
- return response.retry_timeout;
- } else if (response.error != ERROR_NONE) {
- return -EINVAL;
- }
-
- *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
- *enrolled_password_handle_length = response.enrolled_password_handle.length;
- return 0;
-}
-
-int SoftGateKeeperDevice::verify(uint32_t uid,
- uint64_t challenge, const uint8_t *enrolled_password_handle,
- uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
- uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
- bool *request_reenroll) {
-
- if (enrolled_password_handle == NULL ||
- provided_password == NULL) {
- return -EINVAL;
- }
-
- SizedBuffer password_handle_buffer(enrolled_password_handle_length);
- memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
- enrolled_password_handle_length);
- SizedBuffer provided_password_buffer(provided_password_length);
- memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
-
- VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
- VerifyResponse response;
-
- impl_->Verify(request, &response);
-
- if (response.error == ERROR_RETRY) {
- return response.retry_timeout;
- } else if (response.error != ERROR_NONE) {
- return -EINVAL;
- }
-
- if (auth_token != NULL && auth_token_length != NULL) {
- *auth_token = response.auth_token.buffer.release();
- *auth_token_length = response.auth_token.length;
- }
-
- if (request_reenroll != NULL) {
- *request_reenroll = response.request_reenroll;
- }
-
- return 0;
-}
-} // namespace android
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
deleted file mode 100644
index e3dc068..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SOFT_GATEKEEPER_DEVICE_H_
-#define SOFT_GATEKEEPER_DEVICE_H_
-
-#include "SoftGateKeeper.h"
-
-#include <memory>
-
-using namespace gatekeeper;
-
-namespace android {
-
-/**
- * Software based GateKeeper implementation
- */
-class SoftGateKeeperDevice {
-public:
- SoftGateKeeperDevice() {
- impl_.reset(new SoftGateKeeper());
- }
-
- // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API.
-
- /**
- * Enrolls password_payload, which should be derived from a user selected pin or password,
- * with the authentication factor private key used only for enrolling authentication
- * factor data.
- *
- * Returns: 0 on success or an error code less than 0 on error.
- * On error, enrolled_password_handle will not be allocated.
- */
- int enroll(uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
-
- /**
- * Verifies provided_password matches enrolled_password_handle.
- *
- * Implementations of this module may retain the result of this call
- * to attest to the recency of authentication.
- *
- * On success, writes the address of a verification token to auth_token,
- * usable to attest password verification to other trusted services. Clients
- * may pass NULL for this value.
- *
- * Returns: 0 on success or an error code less than 0 on error
- * On error, verification token will not be allocated
- */
- int verify(uint32_t uid, uint64_t challenge,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
-private:
- std::unique_ptr<SoftGateKeeper> impl_;
-};
-
-} // namespace gatekeeper
-
-#endif //SOFT_GATEKEEPER_DEVICE_H_
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
similarity index 71%
rename from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
rename to gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
index 5d0d924..097bb54 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
@@ -14,16 +14,11 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+package android.service.gatekeeper;
-#include <stdint.h>
-#include <string>
+/**
+ * Response object for a GateKeeper verification request.
+ * @hide
+ */
+parcelable GateKeeperResponse cpp_header "gatekeeper/GateKeeperResponse.h";
-namespace unwindstack {
-class Memory;
-}
-
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
new file mode 100644
index 0000000..57adaba
--- /dev/null
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.gatekeeper;
+
+import android.service.gatekeeper.GateKeeperResponse;
+
+/**
+ * Interface for communication with GateKeeper, the
+ * secure password storage daemon.
+ *
+ * This must be kept manually in sync with system/core/gatekeeperd
+ * until AIDL can generate both C++ and Java bindings.
+ *
+ * @hide
+ */
+interface IGateKeeperService {
+ /**
+ * Enrolls a password, returning the handle to the enrollment to be stored locally.
+ * @param uid The Android user ID associated to this enrollment
+ * @param currentPasswordHandle The previously enrolled handle, or null if none
+ * @param currentPassword The previously enrolled plaintext password, or null if none.
+ * If provided, must verify against the currentPasswordHandle.
+ * @param desiredPassword The new desired password, for which a handle will be returned
+ * upon success.
+ * @return an EnrollResponse or null on failure
+ */
+ GateKeeperResponse enroll(int uid, in @nullable byte[] currentPasswordHandle,
+ in @nullable byte[] currentPassword, in byte[] desiredPassword);
+
+ /**
+ * Verifies an enrolled handle against a provided, plaintext blob.
+ * @param uid The Android user ID associated to this enrollment
+ * @param enrolledPasswordHandle The handle against which the provided password will be
+ * verified.
+ * @param The plaintext blob to verify against enrolledPassword.
+ * @return a VerifyResponse, or null on failure.
+ */
+ GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+
+ /**
+ * Verifies an enrolled handle against a provided, plaintext blob.
+ * @param uid The Android user ID associated to this enrollment
+ * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+ * authentication occurs, this value will be written to the returned
+ * authentication attestation.
+ * @param enrolledPasswordHandle The handle against which the provided password will be
+ * verified.
+ * @param The plaintext blob to verify against enrolledPassword.
+ * @return a VerifyResponse with an attestation, or null on failure.
+ */
+ GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
+ in byte[] providedPassword);
+
+ /**
+ * Retrieves the secure identifier for the user with the provided Android ID,
+ * or 0 if none is found.
+ * @param uid the Android user id
+ */
+ long getSecureUserId(int uid);
+
+ /**
+ * Clears secure user id associated with the provided Android ID.
+ * Must be called when password is set to NONE.
+ * @param uid the Android user id.
+ */
+ void clearSecureUserId(int uid);
+
+ /**
+ * Notifies gatekeeper that device setup has been completed and any potentially still existing
+ * state from before a factory reset can be cleaned up (if it has not been already).
+ */
+ void reportDeviceSetupComplete();
+}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index f2818f3..1d65b1c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -16,7 +16,8 @@
#define LOG_TAG "gatekeeperd"
-#include "IGateKeeperService.h"
+#include <android/service/gatekeeper/BnGateKeeperService.h>
+#include <gatekeeper/GateKeeperResponse.h>
#include <errno.h>
#include <fcntl.h>
@@ -25,7 +26,9 @@
#include <unistd.h>
#include <memory>
-#include <android/security/IKeystoreService.h>
+#include <android/security/keystore/IKeystoreService.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
@@ -34,12 +37,11 @@
#include <hardware/hw_auth_token.h>
#include <keystore/keystore.h> // For error code
#include <keystore/keystore_return_types.h>
+#include <libgsi/libgsi.h>
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String16.h>
-#include "SoftGateKeeperDevice.h"
-
#include <hidl/HidlSupport.h>
#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
@@ -49,6 +51,11 @@
using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
using android::hardware::Return;
+using ::android::binder::Status;
+using ::android::service::gatekeeper::BnGateKeeperService;
+using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
+using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
+
namespace android {
static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
@@ -59,10 +66,10 @@
GateKeeperProxy() {
clear_state_if_needed_done = false;
hw_device = IGatekeeper::getService();
+ is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
- if (hw_device == nullptr) {
- ALOGW("falling back to software GateKeeper");
- soft_device.reset(new SoftGateKeeperDevice());
+ if (!hw_device) {
+ LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
}
}
@@ -86,9 +93,9 @@
return;
}
- if (mark_cold_boot()) {
+ if (mark_cold_boot() && !is_running_gsi) {
ALOGI("cold boot: clearing state");
- if (hw_device != nullptr) {
+ if (hw_device) {
hw_device->deleteAllUsers([](const GatekeeperResponse &){});
}
}
@@ -138,16 +145,28 @@
}
}
- virtual int enroll(uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+ // This should only be called on uids being passed to the GateKeeper HAL. It ensures that
+ // secure storage shared across a GSI image and a host image will not overlap.
+ uint32_t adjust_uid(uint32_t uid) {
+ static constexpr uint32_t kGsiOffset = 1000000;
+ CHECK(uid < kGsiOffset);
+ CHECK(hw_device != nullptr);
+ if (is_running_gsi) {
+ return uid + kGsiOffset;
+ }
+ return uid;
+ }
+
+#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
+
+ Status enroll(int32_t uid, const std::unique_ptr<std::vector<uint8_t>>& currentPasswordHandle,
+ const std::unique_ptr<std::vector<uint8_t>>& currentPassword,
+ const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
- return PERMISSION_DENIED;
+ return GK_ERROR;
}
// Make sure to clear any state from before factory reset as soon as a credential is
@@ -155,222 +174,189 @@
clear_state_if_needed();
// need a desired password to enroll
- if (desired_password_length == 0) return -EINVAL;
+ if (desiredPassword.size() == 0) return GK_ERROR;
- int ret;
- if (hw_device != nullptr) {
- const gatekeeper::password_handle_t *handle =
- reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
+ if (!hw_device) {
+ LOG(ERROR) << "has no HAL to talk to";
+ return GK_ERROR;
+ }
- if (handle != NULL && handle->version != 0 && !handle->hardware_backed) {
- // handle is being re-enrolled from a software version. HAL probably won't accept
- // the handle as valid, so we nullify it and enroll from scratch
- current_password_handle = NULL;
- current_password_handle_length = 0;
- current_password = NULL;
- current_password_length = 0;
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ android::hardware::hidl_vec<uint8_t> curPwd;
+
+ if (currentPasswordHandle && currentPassword) {
+ if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
+ return GK_ERROR;
}
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
+ currentPasswordHandle->size());
+ curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
+ currentPassword->size());
+ }
- android::hardware::hidl_vec<uint8_t> curPwdHandle;
- curPwdHandle.setToExternal(const_cast<uint8_t*>(current_password_handle),
- current_password_handle_length);
- android::hardware::hidl_vec<uint8_t> curPwd;
- curPwd.setToExternal(const_cast<uint8_t*>(current_password),
- current_password_length);
- android::hardware::hidl_vec<uint8_t> newPwd;
- newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
- desired_password_length);
+ android::hardware::hidl_vec<uint8_t> newPwd;
+ newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
- Return<void> hwRes = hw_device->enroll(uid, curPwdHandle, curPwd, newPwd,
- [&ret, enrolled_password_handle, enrolled_password_handle_length]
- (const GatekeeperResponse &rsp) {
- ret = static_cast<int>(rsp.code); // propagate errors
- if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
- if (enrolled_password_handle != nullptr &&
- enrolled_password_handle_length != nullptr) {
- *enrolled_password_handle = new uint8_t[rsp.data.size()];
- *enrolled_password_handle_length = rsp.data.size();
- memcpy(*enrolled_password_handle, rsp.data.data(),
- *enrolled_password_handle_length);
+ uint32_t hw_uid = adjust_uid(uid);
+ Return<void> hwRes = hw_device->enroll(
+ hw_uid, curPwdHandle, curPwd, newPwd, [&gkResponse](const GatekeeperResponse& rsp) {
+ if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+ *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+ } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+ rsp.timeout > 0) {
+ *gkResponse = GKResponse::retry(rsp.timeout);
+ } else {
+ *gkResponse = GKResponse::error();
}
- ret = 0; // all success states are reported as 0
- } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
- ret = rsp.timeout;
- }
- });
- if (!hwRes.isOk()) {
- ALOGE("enroll transaction failed\n");
- ret = -1;
+ });
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "enroll transaction failed";
+ return GK_ERROR;
+ }
+
+ if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
+ if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(ERROR) << "HAL returned password handle of invalid length "
+ << gkResponse->payload().size();
+ return GK_ERROR;
}
- } else {
- ret = soft_device->enroll(uid,
- current_password_handle, current_password_handle_length,
- current_password, current_password_length,
- desired_password, desired_password_length,
- enrolled_password_handle, enrolled_password_handle_length);
- }
- if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
- *enrolled_password_handle_length != sizeof(password_handle_t))) {
- ret = GATEKEEPER_RESPONSE_ERROR;
- ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
- *enrolled_password_handle, *enrolled_password_handle_length);
- }
-
- if (ret == GATEKEEPER_RESPONSE_OK) {
- gatekeeper::password_handle_t *handle =
- reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
+ const gatekeeper::password_handle_t* handle =
+ reinterpret_cast<const gatekeeper::password_handle_t*>(
+ gkResponse->payload().data());
store_sid(uid, handle->user_id);
- bool rr;
+ GKResponse verifyResponse;
// immediately verify this password so we don't ask the user to enter it again
// if they just created it.
- verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
- desired_password_length, &rr);
+ auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
+ if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
+ LOG(ERROR) << "Failed to verify password after enrolling";
+ }
}
- return ret;
+ return Status::ok();
}
- virtual int verify(uint32_t uid,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
- uint8_t *auth_token = nullptr;
- uint32_t auth_token_length;
- int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
- provided_password, provided_password_length,
- &auth_token, &auth_token_length, request_reenroll);
- delete [] auth_token;
- return ret;
+ Status verify(int32_t uid, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+ const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
+ return verifyChallenge(uid, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+ gkResponse);
}
- virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+ Status verifyChallenge(int32_t uid, int64_t challenge,
+ const std::vector<uint8_t>& enrolledPasswordHandle,
+ const std::vector<uint8_t>& providedPassword,
+ GKResponse* gkResponse) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
- return PERMISSION_DENIED;
+ return GK_ERROR;
}
// can't verify if we're missing either param
- if ((enrolled_password_handle_length | provided_password_length) == 0)
- return -EINVAL;
+ if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
- int ret;
- if (hw_device != nullptr) {
- const gatekeeper::password_handle_t *handle =
- reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
- // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
- // a HAL if there was none before
- if (handle->version == 0 || handle->hardware_backed) {
- android::hardware::hidl_vec<uint8_t> curPwdHandle;
- curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
- enrolled_password_handle_length);
- android::hardware::hidl_vec<uint8_t> enteredPwd;
- enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
- provided_password_length);
- Return<void> hwRes = hw_device->verify(uid, challenge, curPwdHandle, enteredPwd,
- [&ret, request_reenroll, auth_token, auth_token_length]
- (const GatekeeperResponse &rsp) {
- ret = static_cast<int>(rsp.code); // propagate errors
- if (auth_token != nullptr && auth_token_length != nullptr &&
- rsp.code >= GatekeeperStatusCode::STATUS_OK) {
- *auth_token = new uint8_t[rsp.data.size()];
- *auth_token_length = rsp.data.size();
- memcpy(*auth_token, rsp.data.data(), *auth_token_length);
- if (request_reenroll != nullptr) {
- *request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL);
- }
- ret = 0; // all success states are reported as 0
- } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
- rsp.timeout > 0) {
- ret = rsp.timeout;
+ if (!hw_device) return GK_ERROR;
+
+ if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+ LOG(INFO) << "Password handle has wrong length";
+ return GK_ERROR;
+ }
+ const gatekeeper::password_handle_t* handle =
+ reinterpret_cast<const gatekeeper::password_handle_t*>(
+ enrolledPasswordHandle.data());
+
+ uint32_t hw_uid = adjust_uid(uid);
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
+ enrolledPasswordHandle.size());
+ android::hardware::hidl_vec<uint8_t> enteredPwd;
+ enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
+ providedPassword.size());
+
+ Return<void> hwRes = hw_device->verify(
+ hw_uid, challenge, curPwdHandle, enteredPwd,
+ [&gkResponse](const GatekeeperResponse& rsp) {
+ if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+ *gkResponse = GKResponse::ok(
+ {rsp.data.begin(), rsp.data.end()},
+ rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+ } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
+ *gkResponse = GKResponse::retry(rsp.timeout);
+ } else {
+ *gkResponse = GKResponse::error();
}
});
- if (!hwRes.isOk()) {
- ALOGE("verify transaction failed\n");
- ret = -1;
- }
- } else {
- // upgrade scenario, a HAL has been added to this device where there was none before
- SoftGateKeeperDevice soft_dev;
- ret = soft_dev.verify(uid, challenge,
- enrolled_password_handle, enrolled_password_handle_length,
- provided_password, provided_password_length, auth_token, auth_token_length,
- request_reenroll);
- if (ret == 0) {
- // success! re-enroll with HAL
- *request_reenroll = true;
+ if (!hwRes.isOk()) {
+ LOG(ERROR) << "verify transaction failed";
+ return GK_ERROR;
+ }
+
+ if (gkResponse->response_code() == GKResponseCode::OK) {
+ if (gkResponse->payload().size() != 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+ sp<security::keystore::IKeystoreService> service =
+ interface_cast<security::keystore::IKeystoreService>(binder);
+
+ if (service) {
+ int result = 0;
+ auto binder_result = service->addAuthToken(gkResponse->payload(), &result);
+ if (!binder_result.isOk() ||
+ !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+ LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
+ }
+ } else {
+ LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore.";
}
}
- } else {
- ret = soft_device->verify(uid, challenge,
- enrolled_password_handle, enrolled_password_handle_length,
- provided_password, provided_password_length, auth_token, auth_token_length,
- request_reenroll);
+
+ maybe_store_sid(uid, handle->user_id);
}
- if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) {
- // TODO: cache service?
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<security::IKeystoreService> service =
- interface_cast<security::IKeystoreService>(binder);
- if (service != NULL) {
- std::vector<uint8_t> auth_token_vector(*auth_token,
- (*auth_token) + *auth_token_length);
- int result = 0;
- auto binder_result = service->addAuthToken(auth_token_vector, &result);
- if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
- ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
- }
- } else {
- ALOGE("Unable to communicate with KeyStore");
- }
- }
-
- if (ret == 0) {
- maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>(
- enrolled_password_handle)->user_id);
- }
-
- return ret;
+ return Status::ok();
}
- virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
+ Status getSecureUserId(int32_t uid, int64_t* sid) override {
+ *sid = read_sid(uid);
+ return Status::ok();
+ }
- virtual void clearSecureUserId(uint32_t uid) {
+ Status clearSecureUserId(int32_t uid) override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
- return;
+ return Status::ok();
}
clear_sid(uid);
- if (hw_device != nullptr) {
- hw_device->deleteUser(uid, [] (const GatekeeperResponse &){});
+ if (hw_device) {
+ uint32_t hw_uid = adjust_uid(uid);
+ hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
}
+ return Status::ok();
}
- virtual void reportDeviceSetupComplete() {
+ Status reportDeviceSetupComplete() override {
IPCThreadState* ipc = IPCThreadState::self();
const int calling_pid = ipc->getCallingPid();
const int calling_uid = ipc->getCallingUid();
if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
- return;
+ return Status::ok();
}
clear_state_if_needed();
+ return Status::ok();
}
- virtual status_t dump(int fd, const Vector<String16> &) {
+ status_t dump(int fd, const Vector<String16>&) override {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -391,9 +377,9 @@
private:
sp<IGatekeeper> hw_device;
- std::unique_ptr<SoftGateKeeperDevice> soft_device;
bool clear_state_if_needed_done;
+ bool is_running_gsi;
};
}// namespace android
diff --git a/gatekeeperd/include/gatekeeper/GateKeeperResponse.h b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
new file mode 100644
index 0000000..99fff02
--- /dev/null
+++ b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
@@ -0,0 +1,85 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+#define GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+enum class ResponseCode : int32_t {
+ ERROR = -1,
+ OK = 0,
+ RETRY = 1,
+};
+
+class GateKeeperResponse : public ::android::Parcelable {
+ GateKeeperResponse(ResponseCode response_code, int32_t timeout = 0,
+ std::vector<uint8_t> payload = {}, bool should_reenroll = false)
+ : response_code_(response_code),
+ timeout_(timeout),
+ payload_(std::move(payload)),
+ should_reenroll_(should_reenroll) {}
+
+ public:
+ GateKeeperResponse() = default;
+ GateKeeperResponse(GateKeeperResponse&&) = default;
+ GateKeeperResponse(const GateKeeperResponse&) = default;
+ GateKeeperResponse& operator=(GateKeeperResponse&&) = default;
+
+ static GateKeeperResponse error() { return GateKeeperResponse(ResponseCode::ERROR); }
+ static GateKeeperResponse retry(int32_t timeout) {
+ return GateKeeperResponse(ResponseCode::RETRY, timeout);
+ }
+ static GateKeeperResponse ok(std::vector<uint8_t> payload, bool reenroll = false) {
+ return GateKeeperResponse(ResponseCode::OK, 0, std::move(payload), reenroll);
+ }
+
+ status_t readFromParcel(const Parcel* in) override;
+ status_t writeToParcel(Parcel* out) const override;
+
+ const std::vector<uint8_t>& payload() const { return payload_; }
+
+ void payload(std::vector<uint8_t> payload) { payload_ = payload; }
+
+ ResponseCode response_code() const { return response_code_; }
+
+ void response_code(ResponseCode response_code) { response_code_ = response_code; }
+
+ bool should_reenroll() const { return should_reenroll_; }
+
+ void should_reenroll(bool should_reenroll) { should_reenroll_ = should_reenroll; }
+
+ int32_t timeout() const { return timeout_; }
+
+ void timeout(int32_t timeout) { timeout_ = timeout; }
+
+ private:
+ ResponseCode response_code_;
+ int32_t timeout_;
+ std::vector<uint8_t> payload_;
+ bool should_reenroll_;
+};
+
+} // namespace gatekeeper
+} // namespace service
+} // namespace android
+
+#endif // GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk
deleted file mode 100644
index c38c64b..0000000
--- a/gatekeeperd/tests/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := gatekeeperd-unit-tests
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
-LOCAL_STATIC_LIBRARIES := libscrypt_static
-LOCAL_C_INCLUDES := external/scrypt/lib/crypto
-LOCAL_SRC_FILES := \
- gatekeeper_test.cpp
-include $(BUILD_NATIVE_TEST)
-
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
deleted file mode 100644
index 100375f..0000000
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <iostream>
-
-#include <gtest/gtest.h>
-#include <hardware/hw_auth_token.h>
-
-#include "../SoftGateKeeper.h"
-
-using ::gatekeeper::SizedBuffer;
-using ::testing::Test;
-using ::gatekeeper::EnrollRequest;
-using ::gatekeeper::EnrollResponse;
-using ::gatekeeper::VerifyRequest;
-using ::gatekeeper::VerifyResponse;
-using ::gatekeeper::SoftGateKeeper;
-using ::gatekeeper::secure_id_t;
-
-static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) {
- SizedBuffer password;
-
- password.buffer.reset(new uint8_t[16]);
- password.length = 16;
- memset(password.buffer.get(), 0, 16);
- EnrollRequest request(0, NULL, &password, NULL);
-
- gatekeeper.Enroll(request, response);
-}
-
-TEST(GateKeeperTest, EnrollSuccess) {
- SoftGateKeeper gatekeeper;
- EnrollResponse response;
- do_enroll(gatekeeper, &response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-}
-
-TEST(GateKeeperTest, EnrollBogusData) {
- SoftGateKeeper gatekeeper;
- SizedBuffer password;
- EnrollResponse response;
-
- EnrollRequest request(0, NULL, &password, NULL);
-
- gatekeeper.Enroll(request, &response);
-
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
-
-TEST(GateKeeperTest, VerifySuccess) {
- SoftGateKeeper gatekeeper;
- SizedBuffer provided_password;
- EnrollResponse enroll_response;
-
- provided_password.buffer.reset(new uint8_t[16]);
- provided_password.length = 16;
- memset(provided_password.buffer.get(), 0, 16);
-
- do_enroll(gatekeeper, &enroll_response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
- VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle,
- &provided_password);
- VerifyResponse response;
-
- gatekeeper.Verify(request, &response);
-
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-
- hw_auth_token_t *auth_token =
- reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
- ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type));
- ASSERT_EQ((uint64_t) 1, auth_token->challenge);
- ASSERT_NE(~((uint32_t) 0), auth_token->timestamp);
- ASSERT_NE((uint64_t) 0, auth_token->user_id);
- ASSERT_NE((uint64_t) 0, auth_token->authenticator_id);
-}
-
-TEST(GateKeeperTest, TrustedReEnroll) {
- SoftGateKeeper gatekeeper;
- SizedBuffer provided_password;
- EnrollResponse enroll_response;
- SizedBuffer password_handle;
-
- // do_enroll enrolls an all 0 password
- provided_password.buffer.reset(new uint8_t[16]);
- provided_password.length = 16;
- memset(provided_password.buffer.get(), 0, 16);
- do_enroll(gatekeeper, &enroll_response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
- // keep a copy of the handle
- password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]);
- password_handle.length = enroll_response.enrolled_password_handle.length;
- memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(),
- password_handle.length);
-
- // verify first password
- VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
- &provided_password);
- VerifyResponse response;
- gatekeeper.Verify(request, &response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
- hw_auth_token_t *auth_token =
- reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
- secure_id_t secure_id = auth_token->user_id;
-
- // enroll new password
- provided_password.buffer.reset(new uint8_t[16]);
- provided_password.length = 16;
- memset(provided_password.buffer.get(), 0, 16);
- SizedBuffer password;
- password.buffer.reset(new uint8_t[16]);
- memset(password.buffer.get(), 1, 16);
- password.length = 16;
- EnrollRequest enroll_request(0, &password_handle, &password, &provided_password);
- gatekeeper.Enroll(enroll_request, &enroll_response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
- // verify new password
- password.buffer.reset(new uint8_t[16]);
- memset(password.buffer.get(), 1, 16);
- password.length = 16;
- VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
- &password);
- gatekeeper.Verify(new_request, &response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
- ASSERT_EQ(secure_id,
- reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, UntrustedReEnroll) {
- SoftGateKeeper gatekeeper;
- SizedBuffer provided_password;
- EnrollResponse enroll_response;
-
- // do_enroll enrolls an all 0 password
- provided_password.buffer.reset(new uint8_t[16]);
- provided_password.length = 16;
- memset(provided_password.buffer.get(), 0, 16);
- do_enroll(gatekeeper, &enroll_response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
- // verify first password
- VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
- &provided_password);
- VerifyResponse response;
- gatekeeper.Verify(request, &response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
- hw_auth_token_t *auth_token =
- reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
- secure_id_t secure_id = auth_token->user_id;
-
- // enroll new password
- SizedBuffer password;
- password.buffer.reset(new uint8_t[16]);
- memset(password.buffer.get(), 1, 16);
- password.length = 16;
- EnrollRequest enroll_request(0, NULL, &password, NULL);
- gatekeeper.Enroll(enroll_request, &enroll_response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
- // verify new password
- password.buffer.reset(new uint8_t[16]);
- memset(password.buffer.get(), 1, 16);
- password.length = 16;
- VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
- &password);
- gatekeeper.Verify(new_request, &response);
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
- ASSERT_NE(secure_id,
- reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, VerifyBogusData) {
- SoftGateKeeper gatekeeper;
- SizedBuffer provided_password;
- SizedBuffer password_handle;
- VerifyResponse response;
-
- VerifyRequest request(0, 0, &provided_password, &password_handle);
-
- gatekeeper.Verify(request, &response);
-
- ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 6b00f81..53be526 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -84,3 +84,81 @@
"manifest_healthd.xml"
],
}
+
+cc_library_static {
+ name: "libhealthd_charger_nops",
+
+ srcs: [
+ "healthd_mode_charger_nops.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libhealthd_headers",
+ ],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ ],
+
+ shared_libs: [
+ "android.hardware.health@2.0",
+ "libutils",
+ ],
+}
+
+sysprop_library {
+ name: "charger_sysprop",
+ srcs: ["charger.sysprop"],
+ property_owner: "Platform",
+ api_packages: ["android.sysprop"],
+}
+
+cc_library_static {
+ name: "libhealthd_draw",
+ export_include_dirs: ["."],
+ static_libs: [
+ "libcharger_sysprop",
+ "libminui",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ header_libs: ["libbatteryservice_headers"],
+
+ srcs: ["healthd_draw.cpp"],
+}
+
+cc_library_static {
+ name: "libhealthd_charger",
+ local_include_dirs: ["include"],
+ export_include_dirs: [".", "include"],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ "android.hardware.health@1.0-convert",
+ "libcharger_sysprop",
+ "libhealthstoragedefault",
+ "libhealthd_draw",
+ "libminui",
+ ],
+
+ shared_libs: [
+ "android.hardware.health@2.0",
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libpng",
+ "libsuspend",
+ "libutils",
+ ],
+
+ srcs: [
+ "healthd_mode_charger.cpp",
+ "AnimationParser.cpp",
+ ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 80bf84a..b87f3c7 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,75 +2,6 @@
LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libhealthd_draw
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := \
- libminui \
- libbase
-LOCAL_SRC_FILES := healthd_draw.cpp
-
-ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=$(TARGET_HEALTHD_DRAW_SPLIT_SCREEN)
-else
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=0
-endif
-
-ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_OFFSET),)
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=$(TARGET_HEALTHD_DRAW_SPLIT_OFFSET)
-else
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
-endif
-
-LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS := -Werror
-ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
-LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
-endif
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-endif
-
-LOCAL_SRC_FILES := \
- healthd_mode_charger.cpp \
- AnimationParser.cpp
-
-LOCAL_MODULE := libhealthd_charger
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(LOCAL_PATH) \
- $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
- android.hardware.health@2.0 \
- android.hardware.health@2.0-impl \
- android.hardware.health@1.0 \
- android.hardware.health@1.0-convert \
- libhealthstoragedefault \
- libminui \
- libpng \
- libz \
- libutils \
- libbase \
- libcutils \
- libhealthd_draw \
- liblog \
- libm \
- libc \
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
### charger ###
include $(CLEAR_VARS)
ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
@@ -81,75 +12,107 @@
charger.cpp \
LOCAL_MODULE := charger
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Werror
-ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_CFLAGS += -DCHARGER_NO_UI
-endif
CHARGER_STATIC_LIBRARIES := \
android.hardware.health@2.0-impl \
- android.hardware.health@2.0 \
- android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
libbinderthreadstate \
+ libcharger_sysprop \
+ libhidltransport \
+ libhidlbase \
+ libhwbinder_noltopgo \
+ libhealthstoragedefault \
+ libminui \
+ libvndksupport \
+ libhealthd_charger \
+ libhealthd_charger_nops \
+ libhealthd_draw \
+ libbatterymonitor \
+
+CHARGER_SHARED_LIBRARIES := \
+ android.hardware.health@2.0 \
+ libbase \
+ libcutils \
+ libjsoncpp \
+ libpng \
+ libprocessgroup \
+ liblog \
+ libutils \
+
+CHARGER_SHARED_LIBRARIES += libsuspend
+
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
+
+LOCAL_HAL_STATIC_LIBRARIES := libhealthd
+
+# Symlink /charger to /system/bin/charger
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
+ && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
+
+include $(BUILD_EXECUTABLE)
+
+### charger.recovery ###
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ charger.cpp \
+
+LOCAL_MODULE := charger.recovery
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+LOCAL_MODULE_STEM := charger
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_FORCE_NO_UI=1
+
+# charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
+# any UI support.
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.health@2.0-impl \
+ android.hardware.health@1.0-convert \
+ libbinderthreadstate \
+ libcharger_sysprop \
libhidltransport \
libhidlbase \
libhwbinder_noltopgo \
libhealthstoragedefault \
libvndksupport \
- libhealthd_charger \
- libhealthd_draw \
+ libhealthd_charger_nops \
libbatterymonitor \
+
+# These shared libs will be installed to recovery image because of the dependency in `recovery`
+# module.
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.health@2.0 \
libbase \
- libutils \
libcutils \
liblog \
- libm \
- libc \
+ libutils \
-LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
-
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_STATIC_LIBRARIES += \
- libminui \
- libpng \
- libz \
-
-endif
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
-endif
-
+# The use of LOCAL_HAL_STATIC_LIBRARIES prevents from building this module with Android.bp.
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-# Symlink /charger to /sbin/charger
-LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
- && ln -sf /sbin/charger $(TARGET_ROOT_OUT)/charger
-
include $(BUILD_EXECUTABLE)
+### charger_test ###
include $(CLEAR_VARS)
LOCAL_MODULE := charger_test
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
LOCAL_SRC_FILES := \
- charger.cpp \
charger_test.cpp \
include $(BUILD_EXECUTABLE)
CHARGER_STATIC_LIBRARIES :=
+CHARGER_SHARED_LIBRARIES :=
+### charger_res_images ###
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index 864038b..fde3b95 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -84,7 +84,6 @@
static constexpr const char* fail_prefix = "fail: ";
static constexpr const char* clock_prefix = "clock_display: ";
static constexpr const char* percent_prefix = "percent_display: ";
- static constexpr const char* frame_prefix = "frame: ";
std::vector<animation::frame> frames;
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 2a5667c..06c8176 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -26,6 +26,8 @@
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
+
+#include <algorithm>
#include <memory>
#include <android-base/file.h>
@@ -240,10 +242,9 @@
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str());
- unsigned int i;
double MaxPower = 0;
- for (i = 0; i < mChargerNames.size(); i++) {
+ for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
@@ -477,10 +478,16 @@
while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
+ std::vector<String8>::iterator itIgnoreName;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
+ itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
+ hc->ignorePowerSupplyNames.end(), String8(name));
+ if (itIgnoreName != hc->ignorePowerSupplyNames.end())
+ continue;
+
// Look for "type" file in each subdirectory
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
diff --git a/healthd/OWNERS b/healthd/OWNERS
index 00df08a..d3f8758 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1,2 +1,2 @@
elsk@google.com
-toddpoynor@google.com
+hridya@google.com
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
index dca0ccc..6960c5d 100644
--- a/healthd/android.hardware.health@2.0-service.rc
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -2,4 +2,5 @@
class hal
user system
group system
+ capabilities WAKE_ALARM
file /dev/kmsg w
diff --git a/healthd/animation.h b/healthd/animation.h
index f59fb38..9476c91 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -48,6 +48,25 @@
GRFont* font;
};
+ // When libminui loads PNG images:
+ // - When treating paths as relative paths, it adds ".png" suffix.
+ // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
+ // is added here.
+ void set_resource_root(const std::string& root) {
+ if (!animation_file.empty()) {
+ animation_file = root + animation_file + ".png";
+ }
+ if (!fail_file.empty()) {
+ fail_file = root + fail_file + ".png";
+ }
+ if (!text_clock.font_file.empty()) {
+ text_clock.font_file = root + text_clock.font_file + ".png";
+ }
+ if (!text_percent.font_file.empty()) {
+ text_percent.font_file = root + text_percent.font_file + ".png";
+ }
+ }
+
std::string animation_file;
std::string fail_file;
diff --git a/healthd/api/current.txt b/healthd/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/api/removed.txt b/healthd/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/api/system-current.txt b/healthd/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/api/system-removed.txt b/healthd/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/api/test-current.txt b/healthd/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/api/test-removed.txt b/healthd/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/healthd/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 43e7fd5..58ed416 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,98 +14,18 @@
* limitations under the License.
*/
-#define LOG_TAG "charger"
-#define KLOG_LEVEL 6
+#include "charger.sysprop.h"
+#include "healthd_mode_charger.h"
+#include "healthd_mode_charger_nops.h"
-#include <health2/Health.h>
-#include <healthd/healthd.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <cutils/klog.h>
-
-using namespace android;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Charger mode
-
-extern void healthd_mode_charger_init(struct healthd_config *config);
-extern int healthd_mode_charger_preparetowait(void);
-extern void healthd_mode_charger_heartbeat(void);
-extern void healthd_mode_charger_battery_update(
- struct android::BatteryProperties *props);
-
-// NOPs for modes that need no special action
-
-static void healthd_mode_nop_init(struct healthd_config *config);
-static int healthd_mode_nop_preparetowait(void);
-static void healthd_mode_nop_heartbeat(void);
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops healthd_nops = {
- .init = healthd_mode_nop_init,
- .preparetowait = healthd_mode_nop_preparetowait,
- .heartbeat = healthd_mode_nop_heartbeat,
- .battery_update = healthd_mode_nop_battery_update,
-};
-
-#ifdef CHARGER_NO_UI
-static struct healthd_mode_ops charger_ops = healthd_nops;
-#else
-static struct healthd_mode_ops charger_ops = {
- .init = healthd_mode_charger_init,
- .preparetowait = healthd_mode_charger_preparetowait,
- .heartbeat = healthd_mode_charger_heartbeat,
- .battery_update = healthd_mode_charger_battery_update,
-};
+#ifndef CHARGER_FORCE_NO_UI
+#define CHARGER_FORCE_NO_UI 0
#endif
-static void healthd_mode_nop_init(struct healthd_config* config) {
- using android::hardware::health::V2_0::implementation::Health;
- Health::initInstance(config);
-}
-
-static int healthd_mode_nop_preparetowait(void) {
- return -1;
-}
-
-static void healthd_mode_nop_heartbeat(void) {
-}
-
-static void healthd_mode_nop_battery_update(
- struct android::BatteryProperties* /*props*/) {
-}
-
-int healthd_charger_main(int argc, char** argv) {
- int ch;
-
- healthd_mode_ops = &charger_ops;
-
- while ((ch = getopt(argc, argv, "cr")) != -1) {
- switch (ch) {
- case 'c':
- // -c is now a noop
- break;
- case 'r':
- // force nops for recovery
- healthd_mode_ops = &healthd_nops;
- break;
- case '?':
- default:
- KLOG_ERROR(LOG_TAG, "Unrecognized charger option: %c\n",
- optopt);
- exit(1);
- }
- }
-
- return healthd_main();
-}
-
-#ifndef CHARGER_TEST
int main(int argc, char** argv) {
- return healthd_charger_main(argc, argv);
+ if (CHARGER_FORCE_NO_UI || android::sysprop::ChargerProperties::no_ui().value_or(false)) {
+ return healthd_charger_nops(argc, argv);
+ } else {
+ return healthd_charger_main(argc, argv);
+ }
}
-#endif
diff --git a/healthd/charger.sysprop b/healthd/charger.sysprop
new file mode 100644
index 0000000..b3f47a1
--- /dev/null
+++ b/healthd/charger.sysprop
@@ -0,0 +1,38 @@
+owner: Platform
+module: "android.sysprop.ChargerProperties"
+
+prop {
+ api_name: "draw_split_screen"
+ type: Boolean
+ prop_name: "ro.charger.draw_split_screen"
+ scope: Internal
+ access: Readonly
+}
+prop {
+ api_name: "draw_split_offset"
+ type: Long
+ prop_name: "ro.charger.draw_split_offset"
+ scope: Internal
+ access: Readonly
+}
+prop {
+ api_name: "disable_init_blank"
+ type: Boolean
+ prop_name: "ro.charger.disable_init_blank"
+ scope: Internal
+ access: Readonly
+}
+prop {
+ api_name: "enable_suspend"
+ type: Boolean
+ prop_name: "ro.charger.enable_suspend"
+ scope: Internal
+ access: Readonly
+}
+prop {
+ api_name: "no_ui"
+ type: Boolean
+ prop_name: "ro.charger.no_ui"
+ scope: Internal
+ access: Readonly
+}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 706dc80..50eee19 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -18,15 +18,34 @@
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
+#include "charger.sysprop.h"
#include "healthd_draw.h"
#define LOGE(x...) KLOG_ERROR("charger", x);
#define LOGW(x...) KLOG_WARNING("charger", x);
#define LOGV(x...) KLOG_DEBUG("charger", x);
+static bool get_split_screen() {
+ return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
+}
+
+static int get_split_offset() {
+ int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
+ if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
+ LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
+ std::numeric_limits<int>::min());
+ value = std::numeric_limits<int>::min();
+ }
+ if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
+ LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
+ std::numeric_limits<int>::max());
+ value = std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(value);
+}
+
HealthdDraw::HealthdDraw(animation* anim)
- : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
- kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
+ : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
int ret = gr_init();
if (ret < 0) {
@@ -64,7 +83,8 @@
clear_screen();
/* try to display *something* */
- if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
+ if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||
+ batt_anim->num_frames == 0)
draw_unknown(surf_unknown);
else
draw_battery(batt_anim);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2eb5497..d676083 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -36,17 +36,17 @@
#include <linux/netlink.h>
#include <sys/socket.h>
+#include <cutils/android_get_control_file.h>
#include <cutils/klog.h>
#include <cutils/misc.h>
#include <cutils/properties.h>
#include <cutils/uevent.h>
#include <sys/reboot.h>
-#ifdef CHARGER_ENABLE_SUSPEND
#include <suspend/autosuspend.h>
-#endif
#include "AnimationParser.h"
+#include "charger.sysprop.h"
#include "healthd_draw.h"
#include <health2/Health.h>
@@ -54,6 +54,9 @@
using namespace android;
+// main healthd loop
+extern int healthd_main(void);
+
char* locale;
#ifndef max
@@ -73,6 +76,8 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
+#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME_PROP "ro.product.charger.unplugged_shutdown_time"
#define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -80,8 +85,13 @@
#define LOGW(x...) KLOG_WARNING("charger", x);
#define LOGV(x...) KLOG_DEBUG("charger", x);
-static constexpr const char* animation_desc_path =
- "/res/values/charger/animation.txt";
+// Resources in /product/etc/res overrides resources in /res.
+// If the device is using the Generic System Image (GSI), resources may exist in
+// both paths.
+static constexpr const char* product_animation_desc_path =
+ "/product/etc/res/values/charger/animation.txt";
+static constexpr const char* product_animation_root = "/product/etc/res/images/";
+static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
struct key_state {
bool pending;
@@ -96,6 +106,7 @@
int64_t next_screen_transition;
int64_t next_key_check;
int64_t next_pwr_check;
+ int64_t wait_batt_level_timestamp;
key_state keys[KEY_MAX + 1];
@@ -198,10 +209,9 @@
#define MAX_KLOG_WRITE_BUF_SZ 256
static void dump_last_kmsg(void) {
- char* buf;
+ std::string buf;
char* ptr;
- unsigned sz = 0;
- int len;
+ size_t len;
LOGW("\n");
LOGW("*************** LAST KMSG ***************\n");
@@ -213,21 +223,25 @@
"/proc/last_kmsg",
// clang-format on
};
- for (size_t i = 0; i < arraysize(kmsg); ++i) {
- buf = (char*)load_file(kmsg[i], &sz);
- if (buf && sz) break;
+ for (size_t i = 0; i < arraysize(kmsg) && buf.empty(); ++i) {
+ auto fd = android_get_control_file(kmsg[i]);
+ if (fd >= 0) {
+ android::base::ReadFdToString(fd, &buf);
+ } else {
+ android::base::ReadFileToString(kmsg[i], &buf);
+ }
}
- if (!buf || !sz) {
+ if (buf.empty()) {
LOGW("last_kmsg not found. Cold reset?\n");
goto out;
}
- len = min(sz, LAST_KMSG_MAX_SZ);
- ptr = buf + (sz - len);
+ len = min(buf.size(), LAST_KMSG_MAX_SZ);
+ ptr = &buf[buf.size() - len];
while (len > 0) {
- int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+ size_t cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
char yoink;
char* nl;
@@ -243,26 +257,22 @@
ptr += cnt;
}
- free(buf);
-
out:
LOGW("\n");
LOGW("************* END LAST KMSG *************\n");
LOGW("\n");
}
-#ifdef CHARGER_ENABLE_SUSPEND
static int request_suspend(bool enable) {
+ if (!android::sysprop::ChargerProperties::enable_suspend().value_or(false)) {
+ return 0;
+ }
+
if (enable)
return autosuspend_enable();
else
return autosuspend_disable();
}
-#else
-static int request_suspend(bool /*enable*/) {
- return 0;
-}
-#endif
static void kick_animation(animation* anim) {
anim->run = true;
@@ -280,6 +290,21 @@
if (!batt_anim->run || now < charger->next_screen_transition) return;
+ // If battery level is not ready, keep checking in the defined time
+ if (batt_prop == nullptr ||
+ (batt_prop->batteryLevel == 0 && batt_prop->batteryStatus == BATTERY_STATUS_UNKNOWN)) {
+ if (charger->wait_batt_level_timestamp == 0) {
+ // Set max delay time and skip drawing screen
+ charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
+ LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
+ return;
+ } else if (now <= charger->wait_batt_level_timestamp) {
+ // Do nothing, keep waiting
+ return;
+ }
+ // If timeout and battery level is still not ready, draw unknown battery
+ }
+
if (healthd_draw == nullptr) {
if (healthd_config && healthd_config->screen_on) {
if (!healthd_config->screen_on(batt_prop)) {
@@ -293,10 +318,10 @@
healthd_draw.reset(new HealthdDraw(batt_anim));
-#ifndef CHARGER_DISABLE_INIT_BLANK
- healthd_draw->blank_screen(true);
- charger->screen_blanked = true;
-#endif
+ if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
+ healthd_draw->blank_screen(true);
+ charger->screen_blanked = true;
+ }
}
/* animation is over, blank screen and leave */
@@ -486,6 +511,7 @@
}
static void handle_power_supply_state(charger* charger, int64_t now) {
+ int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
if (!charger->have_battery_state) return;
if (!charger->charger_connected) {
@@ -498,12 +524,14 @@
* Reset & kick animation to show complete animation cycles
* when charger disconnected.
*/
+ timer_shutdown =
+ property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);
charger->next_screen_transition = now - 1;
reset_animation(charger->batt_anim);
kick_animation(charger->batt_anim);
- charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+ charger->next_pwr_check = now + timer_shutdown;
LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
- now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+ now, (int64_t)timer_shutdown, charger->next_pwr_check);
} else if (now >= charger->next_pwr_check) {
LOGW("[%" PRId64 "] shutting down\n", now);
reboot(RB_POWER_OFF);
@@ -600,7 +628,10 @@
bool parse_success;
std::string content;
- if (base::ReadFileToString(animation_desc_path, &content)) {
+ if (base::ReadFileToString(product_animation_desc_path, &content)) {
+ parse_success = parse_animation_desc(content, &battery_animation);
+ battery_animation.set_resource_root(product_animation_root);
+ } else if (base::ReadFileToString(animation_desc_path, &content)) {
parse_success = parse_animation_desc(content, &battery_animation);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
@@ -696,6 +727,7 @@
charger->next_screen_transition = -1;
charger->next_key_check = -1;
charger->next_pwr_check = -1;
+ charger->wait_batt_level_timestamp = 0;
// Initialize Health implementation (which initializes the internal BatteryMonitor).
Health::initInstance(config);
@@ -703,3 +735,33 @@
healthd_config = config;
charger->boot_min_cap = config->boot_min_cap;
}
+
+static struct healthd_mode_ops charger_ops = {
+ .init = healthd_mode_charger_init,
+ .preparetowait = healthd_mode_charger_preparetowait,
+ .heartbeat = healthd_mode_charger_heartbeat,
+ .battery_update = healthd_mode_charger_battery_update,
+};
+
+int healthd_charger_main(int argc, char** argv) {
+ int ch;
+
+ healthd_mode_ops = &charger_ops;
+
+ while ((ch = getopt(argc, argv, "cr")) != -1) {
+ switch (ch) {
+ case 'c':
+ // -c is now a noop
+ break;
+ case 'r':
+ // -r is now a noop
+ break;
+ case '?':
+ default:
+ LOGE("Unrecognized charger option: %c\n", optopt);
+ exit(1);
+ }
+ }
+
+ return healthd_main();
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/healthd/healthd_mode_charger.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to healthd/healthd_mode_charger.h
index 410d379..2f0c9f2 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/healthd/healthd_mode_charger.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
new file mode 100644
index 0000000..bcc04d5
--- /dev/null
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "healthd_mode_charger_nops.h"
+
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+using namespace android;
+
+// main healthd loop
+extern int healthd_main(void);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config* config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* props);
+
+static struct healthd_mode_ops healthd_nops = {
+ .init = healthd_mode_nop_init,
+ .preparetowait = healthd_mode_nop_preparetowait,
+ .heartbeat = healthd_mode_nop_heartbeat,
+ .battery_update = healthd_mode_nop_battery_update,
+};
+
+static void healthd_mode_nop_init(struct healthd_config* config) {
+ using android::hardware::health::V2_0::implementation::Health;
+ Health::initInstance(config);
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+ return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {}
+
+static void healthd_mode_nop_battery_update(struct android::BatteryProperties* /*props*/) {}
+
+int healthd_charger_nops(int /* argc */, char** /* argv */) {
+ healthd_mode_ops = &healthd_nops;
+ return healthd_main();
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/healthd/healthd_mode_charger_nops.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to healthd/healthd_mode_charger_nops.h
index 410d379..a37b247 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/healthd/healthd_mode_charger_nops.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+int healthd_charger_nops(int argc, char** argv);
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index c01e8d7..a900071 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -22,6 +22,8 @@
#include <utils/Errors.h>
#include <utils/String8.h>
+#include <vector>
+
// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
// which healthd wakes up to poll health state and perform periodic chores,
// in units of seconds:
@@ -71,6 +73,7 @@
int (*energyCounter)(int64_t *);
int boot_min_cap;
bool (*screen_on)(android::BatteryProperties *props);
+ std::vector<android::String8> ignorePowerSupplyNames;
};
enum EventWakeup {
diff --git a/init/Android.bp b/init/Android.bp
index ff3b61f..31e4173 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -26,6 +26,7 @@
"-Wextra",
"-Wno-unused-parameter",
"-Werror",
+ "-DALLOW_FIRST_STAGE_CONSOLE=0",
"-DALLOW_LOCAL_PROP_OVERRIDE=0",
"-DALLOW_PERMISSIVE_SELINUX=0",
"-DREBOOT_BOOTLOADER_ON_PANIC=0",
@@ -36,6 +37,8 @@
product_variables: {
debuggable: {
cppflags: [
+ "-UALLOW_FIRST_STAGE_CONSOLE",
+ "-DALLOW_FIRST_STAGE_CONSOLE=1",
"-UALLOW_LOCAL_PROP_OVERRIDE",
"-DALLOW_LOCAL_PROP_OVERRIDE=1",
"-UALLOW_PERMISSIVE_SELINUX",
@@ -60,50 +63,61 @@
},
static_libs: [
"libseccomp_policy",
- "libprocessgroup",
"libavb",
+ "libc++fs",
+ "libcgrouprc_format",
+ "libmodprobe",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
],
shared_libs: [
+ "libbacktrace",
"libbase",
- "libbinder",
"libbootloader_message",
"libcutils",
+ "libcrypto",
"libdl",
"libext4_utils",
"libfs_mgr",
"libfscrypt",
+ "libgsi",
"libhidl-gen-utils",
"libkeyutils",
"liblog",
"liblogwrap",
+ "liblp",
+ "libprocessgroup",
+ "libprocessgroup_setup",
"libselinux",
"libutils",
],
+ bootstrap: true,
}
cc_library_static {
name: "libinit",
recovery_available: true,
- defaults: ["init_defaults"],
+ defaults: ["init_defaults", "selinux_policy_version"],
srcs: [
"action.cpp",
"action_manager.cpp",
"action_parser.cpp",
+ "boringssl_self_test.cpp",
"bootchart.cpp",
"builtins.cpp",
"capabilities.cpp",
- "descriptors.cpp",
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
+ "first_stage_init.cpp",
"first_stage_mount.cpp",
"import_parser.cpp",
"init.cpp",
"keychords.cpp",
"modalias_handler.cpp",
+ "mount_handler.cpp",
+ "mount_namespace.cpp",
"parser.cpp",
"persistent_properties.cpp",
"persistent_properties.proto",
@@ -112,11 +126,16 @@
"reboot.cpp",
"reboot_utils.cpp",
"security.cpp",
+ "selabel.cpp",
"selinux.cpp",
"service.cpp",
+ "service_list.cpp",
+ "service_parser.cpp",
+ "service_utils.cpp",
"sigchld_handler.cpp",
"subcontext.cpp",
"subcontext.proto",
+ "switch_root.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
@@ -124,7 +143,7 @@
"ueventd_parser.cpp",
"util.cpp",
],
- whole_static_libs: ["libcap"],
+ whole_static_libs: ["libcap", "com.android.sysprop.apex"],
header_libs: ["bootimg_headers"],
proto: {
type: "lite",
@@ -139,6 +158,13 @@
},
}
+phony {
+ name: "init",
+ required: [
+ "init_second_stage",
+ ],
+}
+
cc_binary {
name: "init_second_stage",
recovery_available: true,
@@ -175,7 +201,6 @@
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
- "result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
"subcontext_test.cpp",
@@ -202,9 +227,10 @@
genrule {
name: "generated_stub_builtin_function_map",
+ tool_files: ["host_builtin_map.py"],
out: ["generated_stub_builtin_function_map.h"],
- srcs: ["builtins.cpp"],
- cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+ srcs: ["builtins.cpp", "check_builtins.cpp"],
+ cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
}
cc_binary {
@@ -223,28 +249,31 @@
],
whole_static_libs: ["libcap"],
shared_libs: [
- "libprotobuf-cpp-lite",
- "libhidl-gen-utils",
- "libprocessgroup",
- "liblog",
"libcutils",
+ "libhidl-gen-utils",
+ "libjsoncpp",
+ "liblog",
+ "libprocessgroup",
+ "libprotobuf-cpp-lite",
],
srcs: [
"action.cpp",
"action_manager.cpp",
"action_parser.cpp",
"capabilities.cpp",
- "descriptors.cpp",
+ "check_builtins.cpp",
"epoll.cpp",
"keychords.cpp",
"import_parser.cpp",
"host_import_parser.cpp",
"host_init_verifier.cpp",
- "host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"service.cpp",
+ "service_list.cpp",
+ "service_parser.cpp",
+ "service_utils.cpp",
"subcontext.cpp",
"subcontext.proto",
"util.cpp",
@@ -265,5 +294,3 @@
},
},
}
-
-subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index c85727c..006e1bf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,10 +2,13 @@
LOCAL_PATH:= $(call my-dir)
+-include system/sepolicy/policy_version.mk
+
# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
init_options += \
+ -DALLOW_FIRST_STAGE_CONSOLE=1 \
-DALLOW_LOCAL_PROP_OVERRIDE=1 \
-DALLOW_PERMISSIVE_SELINUX=1 \
-DREBOOT_BOOTLOADER_ON_PANIC=1 \
@@ -13,6 +16,7 @@
-DDUMP_ON_UMOUNT_FAILURE=1
else
init_options += \
+ -DALLOW_FIRST_STAGE_CONSOLE=0 \
-DALLOW_LOCAL_PROP_OVERRIDE=0 \
-DALLOW_PERMISSIVE_SELINUX=0 \
-DREBOOT_BOOTLOADER_ON_PANIC=0 \
@@ -28,24 +32,29 @@
-DSHUTDOWN_ZERO_TIMEOUT=0
endif
-init_options += -DLOG_UEVENTS=0
+init_options += -DLOG_UEVENTS=0 \
+ -DSEPOLICY_VERSION=$(POLICYVERS)
init_cflags += \
$(init_options) \
-Wall -Wextra \
-Wno-unused-parameter \
-Werror \
- -std=gnu++1z \
# --
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
devices.cpp \
+ first_stage_init.cpp \
+ first_stage_main.cpp \
first_stage_mount.cpp \
- init_first_stage.cpp \
+ mount_namespace.cpp \
reboot_utils.cpp \
+ selabel.cpp \
selinux.cpp \
switch_root.cpp \
uevent_listener.cpp \
@@ -59,14 +68,23 @@
LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
+# Install adb_debug.prop into debug ramdisk.
+# This allows adb root on a user build, when debug ramdisk is used.
+LOCAL_REQUIRED_MODULES := \
+ adb_debug.prop \
+
# Set up the same mount points on the ramdisk that system-as-root contains.
-LOCAL_POST_INSTALL_CMD := \
- mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+LOCAL_POST_INSTALL_CMD := mkdir -p \
+ $(TARGET_RAMDISK_OUT)/apex \
+ $(TARGET_RAMDISK_OUT)/debug_ramdisk \
+ $(TARGET_RAMDISK_OUT)/dev \
$(TARGET_RAMDISK_OUT)/mnt \
$(TARGET_RAMDISK_OUT)/proc \
$(TARGET_RAMDISK_OUT)/sys \
LOCAL_STATIC_LIBRARIES := \
+ libc++fs \
+ libfs_avb \
libfs_mgr \
libfec \
libfec_rs \
@@ -88,23 +106,27 @@
libz \
libselinux \
libcap \
+ libgsi \
+ libcom.android.sysprop.apex \
+ liblzma \
+ libdexfile_support \
+ libunwindstack \
+ libbacktrace \
+ libmodprobe \
+ libext2_uuid \
LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
include $(BUILD_EXECUTABLE)
+endif
include $(CLEAR_VARS)
LOCAL_MODULE := init_system
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_REQUIRED_MODULES := \
- init_first_stage \
- init_second_stage \
-
-else
LOCAL_REQUIRED_MODULES := \
init_second_stage \
-endif
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
@@ -116,5 +138,3 @@
endif
include $(BUILD_PHONY_PACKAGE)
-
-
diff --git a/init/README.md b/init/README.md
index 6c51b37..2de76a9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -35,29 +35,19 @@
at the beginning of its execution. It is responsible for the initial
set up of the system.
-Devices that mount /system, /vendor through the first stage mount mechanism
-load all of the files contained within the
+Init loads all of the files contained within the
/{system,vendor,odm}/etc/init/ directories immediately after loading
the primary /init.rc. This is explained in more details in the
Imports section of this file.
-Legacy devices without the first stage mount mechanism do the following:
-1. /init.rc imports /init.${ro.hardware}.rc which is the primary
- vendor supplied .rc file.
-2. During the mount\_all command, the init executable loads all of the
- files contained within the /{system,vendor,odm}/etc/init/ directories.
- These directories are intended for all Actions and Services used after
- file system mounting.
-
-One may specify paths in the mount\_all command line to have it import
-.rc files at the specified paths instead of the default ones listed above.
-This is primarily for supporting factory mode and other non-standard boot
-modes. The three default paths should be used for the normal boot process.
+Legacy devices without the first stage mount mechanism previously were
+able to import init scripts during mount_all, however that is deprecated
+and not allowed for devices launching after Q.
The intention of these directories is:
1. /system/etc/init/ is for core system items such as
- SurfaceFlinger, MediaService, and logcatd.
+ SurfaceFlinger, MediaService, and logd.
2. /vendor/etc/init/ is for SoC vendor items such as actions or
daemons needed for core SoC functionality.
3. /odm/etc/init/ is for device manufacturer items such as
@@ -72,7 +62,7 @@
init .rc file should additionally contain any actions associated with
its service.
-An example is the logcatd.rc and Android.mk files located in the
+An example is the userdebug logcatd.rc and Android.mk files located in the
system/core/logcat directory. The LOCAL\_INIT\_RC macro in the
Android.mk file places logcatd.rc in /system/etc/init/ during the
build process. Init loads logcatd.rc during the mount\_all command and
@@ -88,14 +78,6 @@
conflict resolution when multiple services are added to the system, as
each one will go into a separate file.
-There are two options "early" and "late" in mount\_all command
-which can be set after optional paths. With "--early" set, the
-init executable will skip mounting entries with "latemount" flag
-and triggering fs encryption state event. With "--late" set,
-init executable will only mount entries with "latemount" flag but skip
-importing rc files. By default, no option is set, and mount\_all will
-process all entries in the given fstab.
-
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
@@ -161,11 +143,13 @@
Options are modifiers to services. They affect how and when init
runs the service.
-`capabilities <capability> [ <capability>\* ]`
+`capabilities [ <capability>\* ]`
> Set capabilities when exec'ing this service. 'capability' should be a Linux
capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
capabilities.
+ If no capabilities are provided, then all capabilities are removed from this service, even if it
+ runs as root.
`class <name> [ <name>\* ]`
> Specify class names for the service. All services in a
@@ -189,7 +173,7 @@
`critical`
> This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into bootloader.
+ four minutes or before boot completes, the device will reboot into bootloader.
`disabled`
> This service will not automatically start with its class.
@@ -212,9 +196,9 @@
`interface <interface name> <instance name>`
> Associates this service with a list of the HIDL services that it provides. The interface name
- must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
- lazily start services. When multiple interfaces are served, this tag should be used multiple
- times.
+ must be a fully-qualified name and not a value name. For instance, this is used to allow
+ hwservicemanager to lazily start services. When multiple interfaces are served, this tag should
+ be used multiple times.
For example: interface vendor.foo.bar@1.0::IBaz default
`ioprio <class> <priority>`
@@ -235,9 +219,16 @@
to "123,124,125". Since keycodes are handled very early in init,
only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
- which must be equal or greater than 0.
+`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`
+> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`
+ bytes and `limit_percent` which is interpreted as a percentage of the size
+ of the device's physical memory (only if memcg is mounted).
+ Values must be equal or greater than 0.
+
+`memcg.limit_property <value>`
+> Sets the child's memory.limit_in_bytes to the value of the specified property
+ (only if memcg is mounted). This property will override the values specified
+ via `memcg.limit_in_bytes` and `memcg.limit_percent`.
`memcg.soft_limit_in_bytes <value>`
> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
@@ -308,8 +299,9 @@
See the below section on debugging for how this can be used.
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
-> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
- launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
+> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
+ launched process. _type_ must be "dgram", "stream" or "seqpacket". _type_
+ may end with "+passcred" to enable SO_PASSCRED on the socket. User and
group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
seclabel or computed based on the service executable file security context.
@@ -321,6 +313,13 @@
This is particularly useful for creating a periodic service combined with the restart_period
option described above.
+`updatable`
+> Mark that the service can be overridden (via the 'override' option) later in
+ the boot sequence by APEXes. When a service with updatable option is started
+ before APEXes are all activated, the execution is delayed until the activation
+ is finished. A service that is not marked as updatable cannot be overridden by
+ APEXes.
+
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
@@ -396,6 +395,11 @@
not already running. See the start entry for more information on
starting services.
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+ after /data was mounted, and that were running at the time
+ `class_reset_post_data` was called. Only used for FDE devices.
+
`class_stop <serviceclass>`
> Stop and disable all services of the specified class if they are
currently running.
@@ -405,6 +409,10 @@
currently running, without disabling them. They can be restarted
later using `class_start`.
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+ after /data was mounted. Only used for FDE devices.
+
`class_restart <serviceclass>`
> Restarts all services of the specified class.
@@ -464,16 +472,23 @@
-f: force installation of the module even if the version of the running kernel
and the version of the kernel for which the module was compiled do not match.
-`load_all_props`
-> Loads properties from /system, /vendor, et cetera.
- This is included in the default init.rc.
+`load_system_props`
+> (This action is deprecated and no-op.)
`load_persist_props`
> Loads persistent properties when /data has been decrypted.
This is included in the default init.rc.
`loglevel <level>`
-> Sets the kernel log level to level. Properties are expanded within _level_.
+> Sets init's log level to the integer level, from 7 (all logging) to 0
+ (fatal logging only). The numeric values correspond to the kernel log
+ levels, but this command does not affect the kernel log level. Use the
+ `write` command to write to `/proc/sys/kernel/printk` to change that.
+ Properties are expanded within _level_.
+
+`mark_post_data`
+> Used to mark the point right after /data is mounted. Used to implement the
+ `class_reset_post_data` and `class_start_post_data` commands.
`mkdir <path> [mode] [owner] [group]`
> Create a directory at _path_, optionally with the given mode, owner, and
@@ -482,16 +497,22 @@
will be updated if the directory exists already.
`mount_all <fstab> [ <path> ]\* [--<option>]`
-> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab and imports .rc files
- at the specified paths (e.g., on the partitions just mounted) with optional
+> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
options "early" and "late".
- Refer to the section of "Init .rc Files" for detail.
+ With "--early" set, the init executable will skip mounting entries with
+ "latemount" flag and triggering fs encryption state event. With "--late" set,
+ init executable will only mount entries with "latemount" flag. By default,
+ no option is set, and mount\_all will process all entries in the given fstab.
`mount <type> <device> <dir> [ <flag>\* ] [<options>]`
> Attempt to mount the named device at the directory _dir_
_flag_s include "ro", "rw", "remount", "noatime", ...
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
- a comma separated string, eg: barrier=1,noauto\_da\_alloc
+ a comma separated string, e.g. barrier=1,noauto\_da\_alloc
+
+`parse_apex_configs`
+> Parses config file(s) from the mounted APEXes. Intended to be used only once
+ when apexd notifies the mount event by setting apexd.status to ready.
`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
@@ -553,7 +574,7 @@
`symlink <target> <path>`
> Create a symbolic link at _path_ with the value _target_
-`sysclktz <mins_west_of_gmt>`
+`sysclktz <minutes_west_of_gmt>`
> Set the system clock base (0 if system clock ticks in GMT)
`trigger <event>`
@@ -563,9 +584,6 @@
`umount <path>`
> Unmount the filesystem mounted at that path.
-`verity_load_state`
-> Internal implementation detail used to load dm-verity state.
-
`verity_update_state <mount-point>`
> Internal implementation detail used to update dm-verity state and
set the partition._mount-point_.verified properties used by adb remount
@@ -605,8 +623,9 @@
`ro.boot.init_rc` during initial boot.
2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
devices immediately after importing /init.rc.
- 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all.
+ 3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files
+ at specified paths during mount_all, not allowed for devices launching
+ after Q.
The order that files are imported is a bit complex for legacy reasons
and to keep backwards compatibility. It is not strictly guaranteed.
@@ -616,7 +635,7 @@
earlier executed trigger, or 2) place it in an Action with the same
trigger within the same file at an earlier line.
-Nonetheless, the defacto order for first stage mount devices is:
+Nonetheless, the de facto order for first stage mount devices is:
1. /init.rc is parsed then recursively each of its imports are
parsed.
2. The contents of /system/etc/init/ are alphabetized and parsed
@@ -641,12 +660,19 @@
Properties
----------
-Init provides information about the services that it is responsible
-for via the below properties.
+Init provides state information with the following properties.
`init.svc.<name>`
> State of a named service ("stopped", "stopping", "running", "restarting")
+`dev.mnt.blk.<mount_point>`
+> Block device base name associated with a *mount_point*.
+ The *mount_point* has / replaced by . and if referencing the root mount point
+ "/", it will use "/root", specifically `dev.mnt.blk.root`.
+ Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+ `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+ characteristics in a device agnostic manner.
+
Boot timing
-----------
@@ -656,8 +682,11 @@
> Time after boot in ns (via the CLOCK\_BOOTTIME clock) at which the first
stage of init started.
+`ro.boottime.init.first_stage`
+> How long in ns it took to run first stage.
+
`ro.boottime.init.selinux`
-> How long it took the first stage to initialize SELinux.
+> How long in ns it took to run SELinux stage.
`ro.boottime.init.cold_boot_wait`
> How long init waited for ueventd's coldboot phase to end.
@@ -699,7 +728,7 @@
A handy script named compare-bootcharts.py can be used to compare the
start/end time of selected processes. The aforementioned grab-bootchart.sh
will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
-If two such barballs are preserved on the host machine under different
+If two such tarballs are preserved on the host machine under different
directories, the script can list the timestamps differences. For example:
Usage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_
@@ -747,7 +776,7 @@
This option will send SIGSTOP to a service immediately before calling exec. This gives a window
where developers can attach a debugger, strace, etc before continuing the service with SIGCONT.
-This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.
+This flag can also be dynamically controlled via the ctl.sigstop_on and ctl.sigstop_off properties.
Below is an example of dynamically debugging logd via the above:
@@ -795,3 +824,42 @@
SELinux would permit the operation, or if the UIDs and GIDs resolve.
2) No checking if a service exists or has a valid SELinux domain defined
3) No checking if a service has not been previously defined in a different init script.
+
+Early Init Boot Sequence
+------------------------
+The early init boot sequence is broken up into three stages: first stage init, SELinux setup, and
+second stage init.
+
+First stage init is responsible for setting up the bare minimum requirements to load the rest of the
+system. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which
+needs to include all partitions that contain system code, for example system and vendor), and moving
+the system.img mount to / for devices with a ramdisk.
+
+Note that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the
+time first stage init finishes. Android Q will also require dynamic partitions and therefore will
+require using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead
+of a dedicated ramdisk as well.
+
+First stage init has three variations depending on the device configuration:
+1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init
+points to /system/bin/init for backwards compatibility. These devices do not need to do anything to
+mount system.img, since it is by definition already mounted as the rootfs by the kernel.
+
+2) For devices with a ramdisk, first stage init is a static executable located at /init. These
+devices mount system.img as /system then perform a switch root operation to move the mount at
+/system to /. The contents of the ramdisk are freed after mounting has completed.
+
+3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init
+located at /init within the recovery ramdisk. These devices first switch root to
+/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
+as 2). Note that the decision to boot normally into Android instead of booting
+into recovery mode is made if androidboot.force_normal_boot=1 is present in the
+kernel commandline.
+
+Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
+phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
+information on the specifics of this process.
+
+Lastly once that phase finishes, it execs /system/bin/init again with the "second_stage"
+argument. At this point the main phase of init runs and continues the boot process via the init.rc
+scripts.
diff --git a/init/action.cpp b/init/action.cpp
index 94ccef2..65ba25d 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -28,17 +28,18 @@
namespace android {
namespace init {
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
- const std::vector<std::string>& args,
- const std::string& context) {
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args, const std::string& context) {
auto builtin_arguments = BuiltinArguments(context);
builtin_arguments.args.resize(args.size());
builtin_arguments.args[0] = args[0];
for (std::size_t i = 1; i < args.size(); ++i) {
- if (!expand_props(args[i], &builtin_arguments.args[i])) {
- return Error() << "cannot expand '" << args[i] << "'";
+ auto expanded_arg = ExpandProps(args[i]);
+ if (!expanded_arg) {
+ return expanded_arg.error();
}
+ builtin_arguments.args[i] = std::move(*expanded_arg);
}
return function(builtin_arguments);
@@ -51,7 +52,7 @@
args_(std::move(args)),
line_(line) {}
-Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
if (subcontext) {
if (execute_in_subcontext_) {
return subcontext->Execute(args_);
@@ -67,6 +68,30 @@
return RunBuiltinFunction(func_, args_, kInitContext);
}
+Result<void> Command::CheckCommand() const {
+ auto builtin_arguments = BuiltinArguments("host_init_verifier");
+
+ builtin_arguments.args.resize(args_.size());
+ builtin_arguments.args[0] = args_[0];
+ for (size_t i = 1; i < args_.size(); ++i) {
+ auto expanded_arg = ExpandProps(args_[i]);
+ if (!expanded_arg) {
+ if (expanded_arg.error().message().find("doesn't exist while expanding") !=
+ std::string::npos) {
+ // If we failed because we won't have a property, use an empty string, which is
+ // never returned from the parser, to indicate that this field cannot be checked.
+ builtin_arguments.args[i] = "";
+ } else {
+ return expanded_arg.error();
+ }
+ } else {
+ builtin_arguments.args[i] = std::move(*expanded_arg);
+ }
+ }
+
+ return func_(builtin_arguments);
+}
+
std::string Command::BuildCommandString() const {
return Join(args_, ' ');
}
@@ -81,28 +106,43 @@
filename_(filename),
line_(line) {}
-const KeywordFunctionMap* Action::function_map_ = nullptr;
+const BuiltinFunctionMap* Action::function_map_ = nullptr;
-Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
+Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
if (!function_map_) {
return Error() << "no function map available";
}
- auto function = function_map_->FindFunction(args);
- if (!function) return Error() << function.error();
+ auto map_result = function_map_->Find(args);
+ if (!map_result) {
+ return Error() << map_result.error();
+ }
- commands_.emplace_back(function->second, function->first, std::move(args), line);
- return Success();
+ commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),
+ line);
+ return {};
}
void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
- commands_.emplace_back(f, false, std::move(args), line);
+ commands_.emplace_back(std::move(f), false, std::move(args), line);
}
std::size_t Action::NumCommands() const {
return commands_.size();
}
+size_t Action::CheckAllCommands() const {
+ size_t failures = 0;
+ for (const auto& command : commands_) {
+ if (auto result = command.CheckCommand(); !result) {
+ LOG(ERROR) << "Command '" << command.BuildCommandString() << "' (" << filename_ << ":"
+ << command.line() << ") failed: " << result.error();
+ ++failures;
+ }
+ }
+ return failures;
+}
+
void Action::ExecuteOneCommand(std::size_t command) const {
// We need a copy here since some Command execution may result in
// changing commands_ vector by importing .rc files through parser
@@ -127,7 +167,7 @@
// report such failures unless we're running at the DEBUG log level.
bool report_failure = !result.has_value();
if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
- result.error_errno() == ENOENT) {
+ result.error().code() == ENOENT) {
report_failure = false;
}
@@ -139,7 +179,7 @@
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
<< ":" << command.line() << ") took " << duration.count() << "ms and "
- << (result ? "succeeded" : "failed: " + result.error_string());
+ << (result ? "succeeded" : "failed: " + result.error().message());
}
}
diff --git a/init/action.h b/init/action.h
index 967c682..1534bf9 100644
--- a/init/action.h
+++ b/init/action.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_ACTION_H
-#define _INIT_ACTION_H
+#pragma once
#include <map>
#include <queue>
@@ -31,16 +30,17 @@
namespace android {
namespace init {
-Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
- const std::vector<std::string>& args, const std::string& context);
+Result<void> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args, const std::string& context);
class Command {
public:
Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
int line);
- Result<Success> InvokeFunc(Subcontext* subcontext) const;
+ Result<void> InvokeFunc(Subcontext* subcontext) const;
std::string BuildCommandString() const;
+ Result<void> CheckCommand() const;
int line() const { return line_; }
@@ -61,9 +61,9 @@
const std::string& event_trigger,
const std::map<std::string, std::string>& property_triggers);
- Result<Success> AddCommand(std::vector<std::string>&& args, int line);
+ Result<void> AddCommand(std::vector<std::string>&& args, int line);
void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
- std::size_t NumCommands() const;
+ size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
bool CheckEvent(const EventTrigger& event_trigger) const;
@@ -71,11 +71,12 @@
bool CheckEvent(const BuiltinAction& builtin_action) const;
std::string BuildTriggersString() const;
void DumpState() const;
+ size_t CheckAllCommands() const;
bool oneshot() const { return oneshot_; }
const std::string& filename() const { return filename_; }
int line() const { return line_; }
- static void set_function_map(const KeywordFunctionMap* function_map) {
+ static void set_function_map(const BuiltinFunctionMap* function_map) {
function_map_ = function_map;
}
@@ -91,10 +92,8 @@
Subcontext* subcontext_;
std::string filename_;
int line_;
- static const KeywordFunctionMap* function_map_;
+ static const BuiltinFunctionMap* function_map_;
};
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 9de4085..ebca762 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -23,6 +23,14 @@
ActionManager::ActionManager() : current_command_(0) {}
+size_t ActionManager::CheckAllCommands() {
+ size_t failures = 0;
+ for (const auto& action : actions_) {
+ failures += action->CheckAllCommands();
+ }
+ return failures;
+}
+
ActionManager& ActionManager::GetInstance() {
static ActionManager instance;
return instance;
@@ -47,7 +55,7 @@
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
- action->AddCommand(func, {name}, 0);
+ action->AddCommand(std::move(func), {name}, 0);
event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
@@ -88,7 +96,8 @@
current_command_ = 0;
if (action->oneshot()) {
auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
- actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+ actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
+ actions_.end());
}
}
}
diff --git a/init/action_manager.h b/init/action_manager.h
index 5f47a6d..a2b95ac 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_ACTION_MANAGER_H
-#define _INIT_ACTION_MANAGER_H
+#pragma once
#include <string>
#include <vector>
@@ -32,6 +31,7 @@
// Exposed for testing
ActionManager();
+ size_t CheckAllCommands();
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
@@ -55,5 +55,3 @@
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 4f8bd16..ff20e43 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -55,8 +55,8 @@
return CanReadProperty(subcontext->context(), prop_name);
}
-Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
- std::map<std::string, std::string>* property_triggers) {
+Result<void> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+ std::map<std::string, std::string>* property_triggers) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
@@ -74,12 +74,12 @@
if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
return Error() << "multiple property triggers found for same property";
}
- return Success();
+ return {};
}
-Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
- std::string* event_trigger,
- std::map<std::string, std::string>* property_triggers) {
+Result<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+ std::string* event_trigger,
+ std::map<std::string, std::string>* property_triggers) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (args[i].empty()) {
@@ -108,13 +108,13 @@
}
}
- return Success();
+ return {};
}
} // namespace
-Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
return Error() << "Actions must have a trigger";
@@ -142,19 +142,19 @@
property_triggers);
action_ = std::move(action);
- return Success();
+ return {};
}
-Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- return action_ ? action_->AddCommand(std::move(args), line) : Success();
+Result<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ return action_ ? action_->AddCommand(std::move(args), line) : Result<void>{};
}
-Result<Success> ActionParser::EndSection() {
+Result<void> ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
action_manager_->AddAction(std::move(action_));
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/action_parser.h b/init/action_parser.h
index b7f7074..2fe9983 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -32,10 +32,10 @@
public:
ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
: action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
private:
ActionManager* action_manager_;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index c2cf573..b7db9b6 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -165,20 +165,20 @@
LOG(INFO) << "Bootcharting finished";
}
-static Result<Success> do_bootchart_start() {
+static Result<void> do_bootchart_start() {
// We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
std::string start;
if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
LOG(VERBOSE) << "Not bootcharting";
- return Success();
+ return {};
}
g_bootcharting_thread = new std::thread(bootchart_thread_main);
- return Success();
+ return {};
}
-static Result<Success> do_bootchart_stop() {
- if (!g_bootcharting_thread) return Success();
+static Result<void> do_bootchart_stop() {
+ if (!g_bootcharting_thread) return {};
// Tell the worker thread it's time to quit.
{
@@ -190,10 +190,10 @@
g_bootcharting_thread->join();
delete g_bootcharting_thread;
g_bootcharting_thread = nullptr;
- return Success();
+ return {};
}
-Result<Success> do_bootchart(const BuiltinArguments& args) {
+Result<void> do_bootchart(const BuiltinArguments& args) {
if (args[1] == "start") return do_bootchart_start();
return do_bootchart_stop();
}
diff --git a/init/bootchart.h b/init/bootchart.h
index 05474ca..6f19aad 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -26,7 +26,7 @@
namespace android {
namespace init {
-Result<Success> do_bootchart(const BuiltinArguments& args);
+Result<void> do_bootchart(const BuiltinArguments& args);
} // namespace init
} // namespace android
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
new file mode 100644
index 0000000..759eb43
--- /dev/null
+++ b/init/boringssl_self_test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "boringssl_self_test.h"
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <openssl/crypto.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace init {
+
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&) {
+ pid_t id = fork();
+
+ if (id == 0) {
+ if (BORINGSSL_self_test() != 1) {
+ LOG(INFO) << "BoringSSL crypto self tests failed";
+
+ // This check has failed, so the device should refuse
+ // to boot. Rebooting to bootloader to wait for
+ // further action from the user.
+
+ int result = android_reboot(ANDROID_RB_RESTART2, 0,
+ "bootloader,boringssl-self-check-failed");
+ if (result != 0) {
+ LOG(ERROR) << "Failed to reboot into bootloader";
+ }
+ }
+
+ _exit(0);
+ } else if (id == -1) {
+ // Failed to fork, so cannot run the test. Refuse to continue.
+ PLOG(FATAL) << "Failed to fork for BoringSSL self test";
+ }
+
+ return {};
+}
+
+} // namespace init
+} // namespace android
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/init/boringssl_self_test.h
similarity index 73%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to init/boringssl_self_test.h
index 410d379..9e717d0 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/init/boringssl_self_test.h
@@ -14,11 +14,15 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<void> StartBoringSslSelfTest(const BuiltinArguments&);
+
+} // namespace init
+} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 5d62c0b..e75f5cb 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,10 +16,12 @@
#include "builtins.h"
+#include <android/api-level.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
+#include <glob.h>
#include <linux/loop.h>
#include <linux/module.h>
#include <mntent.h>
@@ -40,6 +42,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <ApexProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -53,6 +56,7 @@
#include <fs_mgr.h>
#include <fscrypt/fscrypt.h>
#include <fscrypt/fscrypt_init_extensions.h>
+#include <libgsi/libgsi.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@@ -61,34 +65,42 @@
#include "action_manager.h"
#include "bootchart.h"
#include "init.h"
+#include "mount_namespace.h"
#include "parser.h"
#include "property_service.h"
#include "reboot.h"
#include "rlimit_parser.h"
+#include "selabel.h"
#include "selinux.h"
#include "service.h"
+#include "service_list.h"
#include "subcontext.h"
#include "util.h"
using namespace std::literals::string_literals;
+using android::base::Basename;
using android::base::unique_fd;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::ReadFstabFromFile;
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
namespace android {
namespace init {
+std::vector<std::string> late_import_paths;
+
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
-static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
LOG(ERROR) << "Rebooting into recovery";
std::string err;
if (!write_bootloader_message(options, &err)) {
return Error() << "Failed to set bootloader message: " << err;
}
property_set("sys.powerctl", "reboot,recovery");
- return Success();
+ return {};
}
template <typename F>
@@ -98,7 +110,10 @@
}
}
-static Result<Success> do_class_start(const BuiltinArguments& args) {
+static Result<void> do_class_start(const BuiltinArguments& args) {
+ // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.
+ if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+ return {};
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
@@ -109,32 +124,70 @@
}
}
}
- return Success();
+ return {};
}
-static Result<Success> do_class_stop(const BuiltinArguments& args) {
+static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_start_post_data' only available in init context";
+ }
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+
+ if (!is_apex_updatable) {
+ // No need to start these on devices that don't support APEX, since they're not
+ // stopped either.
+ return {};
+ }
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count(args[1])) {
+ if (auto result = service->StartIfPostData(); !result) {
+ LOG(ERROR) << "Could not start service '" << service->name()
+ << "' as part of class '" << args[1] << "': " << result.error();
+ }
+ }
+ }
+ return {};
+}
+
+static Result<void> do_class_stop(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Stop);
- return Success();
+ return {};
}
-static Result<Success> do_class_reset(const BuiltinArguments& args) {
+static Result<void> do_class_reset(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Reset);
- return Success();
+ return {};
}
-static Result<Success> do_class_restart(const BuiltinArguments& args) {
+static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
+ if (args.context != kInitContext) {
+ return Error() << "command 'class_reset_post_data' only available in init context";
+ }
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+ if (!is_apex_updatable) {
+ // No need to stop these on devices that don't support APEX.
+ return {};
+ }
+ ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+ return {};
+}
+
+static Result<void> do_class_restart(const BuiltinArguments& args) {
+ // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
+ if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
+ return {};
ForEachServiceInClass(args[1], &Service::Restart);
- return Success();
+ return {};
}
-static Result<Success> do_domainname(const BuiltinArguments& args) {
+static Result<void> do_domainname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_enable(const BuiltinArguments& args) {
+static Result<void> do_enable(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "Could not find service";
@@ -142,36 +195,36 @@
return Error() << "Could not enable service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_exec(const BuiltinArguments& args) {
+static Result<void> do_exec(const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
- return Error() << "Could not create exec service";
+ return Error() << "Could not create exec service: " << service.error();
}
- if (auto result = service->ExecStart(); !result) {
+ if (auto result = (*service)->ExecStart(); !result) {
return Error() << "Could not start exec service: " << result.error();
}
- ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ ServiceList::GetInstance().AddService(std::move(*service));
+ return {};
}
-static Result<Success> do_exec_background(const BuiltinArguments& args) {
+static Result<void> do_exec_background(const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
- return Error() << "Could not create exec background service";
+ return Error() << "Could not create exec background service: " << service.error();
}
- if (auto result = service->Start(); !result) {
+ if (auto result = (*service)->Start(); !result) {
return Error() << "Could not start exec background service: " << result.error();
}
- ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ ServiceList::GetInstance().AddService(std::move(*service));
+ return {};
}
-static Result<Success> do_exec_start(const BuiltinArguments& args) {
+static Result<void> do_exec_start(const BuiltinArguments& args) {
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
return Error() << "Service not found";
@@ -181,29 +234,29 @@
return Error() << "Could not start exec service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_export(const BuiltinArguments& args) {
+static Result<void> do_export(const BuiltinArguments& args) {
if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
return ErrnoError() << "setenv() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_hostname(const BuiltinArguments& args) {
+static Result<void> do_hostname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_ifup(const BuiltinArguments& args) {
+static Result<void> do_ifup(const BuiltinArguments& args) {
struct ifreq ifr;
strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
- unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+ unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
if (s < 0) return ErrnoError() << "opening socket failed";
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
@@ -216,10 +269,10 @@
return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_insmod(const BuiltinArguments& args) {
+static Result<void> do_insmod(const BuiltinArguments& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -237,34 +290,34 @@
int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
- return Success();
+ return {};
}
-static Result<Success> do_interface_restart(const BuiltinArguments& args) {
+static Result<void> do_interface_restart(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Restart();
- return Success();
+ return {};
}
-static Result<Success> do_interface_start(const BuiltinArguments& args) {
+static Result<void> do_interface_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
if (auto result = svc->Start(); !result) {
return Error() << "Could not start interface: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_interface_stop(const BuiltinArguments& args) {
+static Result<void> do_interface_stop(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
if (!svc) return Error() << "interface " << args[1] << " not found";
svc->Stop();
- return Success();
+ return {};
}
// mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const BuiltinArguments& args) {
+static Result<void> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -291,7 +344,7 @@
if (args.size() == 5) {
gid = DecodeUid(args[4]);
if (!gid) {
- return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+ return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
}
}
@@ -313,15 +366,15 @@
{"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
}
}
- return Success();
+ return {};
}
/* umount <path> */
-static Result<Success> do_umount(const BuiltinArguments& args) {
+static Result<void> do_umount(const BuiltinArguments& args) {
if (umount(args[1].c_str()) < 0) {
return ErrnoError() << "umount() failed";
}
- return Success();
+ return {};
}
static struct {
@@ -349,7 +402,7 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const BuiltinArguments& args) {
+static Result<void> do_mount(const BuiltinArguments& args) {
const char* options = nullptr;
unsigned flags = 0;
bool wait = false;
@@ -396,7 +449,7 @@
ioctl(loop, LOOP_CLR_FD, 0);
return ErrnoError() << "mount() failed";
}
- return Success();
+ return {};
}
}
}
@@ -411,7 +464,7 @@
}
- return Success();
+ return {};
}
/* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -439,51 +492,6 @@
if (false) DumpState();
}
-/* mount_fstab
- *
- * Call fs_mgr_mount_all() to mount the given fstab
- */
-static Result<int> mount_fstab(const char* fstabfile, int mount_mode) {
- /*
- * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
- * do the call in the child to provide protection to the main init
- * process if anything goes wrong (crash or memory leak), and wait for
- * the child to finish in the parent.
- */
- pid_t pid = fork();
- if (pid > 0) {
- /* Parent. Wait for the child to return */
- int status;
- int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (wp_ret == -1) {
- // Unexpected error code. We will continue anyway.
- PLOG(WARNING) << "waitpid failed";
- }
-
- if (WIFEXITED(status)) {
- return WEXITSTATUS(status);
- } else {
- return Error() << "child aborted";
- }
- } else if (pid == 0) {
- /* child, call fs_mgr_mount_all() */
-
- // So we can always see what fs_mgr_mount_all() does.
- // Only needed if someone explicitly changes the default log level in their init.rc.
- android::base::ScopedLogSeverity info(android::base::INFO);
-
- struct fstab* fstab = fs_mgr_read_fstab(fstabfile);
- int child_ret = fs_mgr_mount_all(fstab, mount_mode);
- fs_mgr_free_fstab(fstab);
- if (child_ret == -1) {
- PLOG(ERROR) << "fs_mgr_mount_all returned an error";
- }
- _exit(child_ret);
- } else {
- return Error() << "fork() failed";
- }
-}
-
/* Queue event based on fs_mgr return code.
*
* code: return code of fs_mgr_mount_all
@@ -493,25 +501,28 @@
*
* return code is processed based on input code
*/
-static Result<Success> queue_fs_event(int code) {
+static Result<void> queue_fs_event(int code) {
if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
+ if (android::gsi::IsGsiRunning()) {
+ return Error() << "cannot wipe within GSI";
+ }
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
return reboot_into_recovery(options);
@@ -526,7 +537,7 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
if (fscrypt_install_keyring()) {
return Error() << "fscrypt_install_keyring() failed";
@@ -537,7 +548,7 @@
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (fscrypt_install_keyring()) {
return Error() << "fscrypt_install_keyring() failed";
@@ -548,7 +559,7 @@
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- return Success();
+ return {};
} else if (code > 0) {
Error() << "fs_mgr_mount_all() returned unexpected error " << code;
}
@@ -562,12 +573,12 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static Result<Success> do_mount_all(const BuiltinArguments& args) {
+static Result<void> do_mount_all(const BuiltinArguments& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
int mount_mode = MOUNT_MODE_DEFAULT;
- const char* fstabfile = args[1].c_str();
+ const auto& fstab_file = args[1];
std::size_t path_arg_end = args.size();
const char* prop_post_fix = "default";
@@ -587,13 +598,20 @@
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
android::base::Timer t;
- auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+
+ Fstab fstab;
+ if (!ReadFstabFromFile(fstab_file, &fstab)) {
+ return Error() << "Could not read fstab";
+ }
+
+ auto mount_fstab_return_code =
+ CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
if (!mount_fstab_return_code) {
- return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
+ return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
}
property_set(prop_name, std::to_string(t.duration().count()));
- if (import_rc) {
+ if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
import_late(args.args, 2, path_arg_end);
}
@@ -607,62 +625,86 @@
}
}
- return Success();
+ return {};
}
-static Result<Success> do_swapon_all(const BuiltinArguments& args) {
- struct fstab *fstab;
- int ret;
+/* umount_all <fstab> */
+static Result<void> do_umount_all(const BuiltinArguments& args) {
+ Fstab fstab;
+ if (!ReadFstabFromFile(args[1], &fstab)) {
+ return Error() << "Could not read fstab";
+ }
- fstab = fs_mgr_read_fstab(args[1].c_str());
- ret = fs_mgr_swapon_all(fstab);
- fs_mgr_free_fstab(fstab);
+ auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_mount_all() " << result.error();
+ }
- if (ret != 0) return Error() << "fs_mgr_swapon_all() failed";
- return Success();
+ if (*result != 0) {
+ return Error() << "fs_mgr_mount_all() failed: " << *result;
+ }
+ return {};
}
-static Result<Success> do_setprop(const BuiltinArguments& args) {
+static Result<void> do_swapon_all(const BuiltinArguments& args) {
+ Fstab fstab;
+ if (!ReadFstabFromFile(args[1], &fstab)) {
+ return Error() << "Could not read fstab '" << args[1] << "'";
+ }
+
+ auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
+ if (!result) {
+ return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
+ }
+
+ if (*result == 0) {
+ return Error() << "fs_mgr_swapon_all() failed.";
+ }
+
+ return {};
+}
+
+static Result<void> do_setprop(const BuiltinArguments& args) {
property_set(args[1], args[2]);
- return Success();
+ return {};
}
-static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+static Result<void> do_setrlimit(const BuiltinArguments& args) {
auto rlimit = ParseRlimit(args.args);
if (!rlimit) return rlimit.error();
if (setrlimit(rlimit->first, &rlimit->second) == -1) {
return ErrnoError() << "setrlimit failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_start(const BuiltinArguments& args) {
+static Result<void> do_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
if (auto result = svc->Start(); !result) {
return Error() << "Could not start service: " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_stop(const BuiltinArguments& args) {
+static Result<void> do_stop(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
- return Success();
+ return {};
}
-static Result<Success> do_restart(const BuiltinArguments& args) {
+static Result<void> do_restart(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
- return Success();
+ return {};
}
-static Result<Success> do_trigger(const BuiltinArguments& args) {
+static Result<void> do_trigger(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
- return Success();
+ return {};
}
static int MakeSymlink(const std::string& target, const std::string& linkpath) {
@@ -683,33 +725,33 @@
return rc;
}
-static Result<Success> do_symlink(const BuiltinArguments& args) {
+static Result<void> do_symlink(const BuiltinArguments& args) {
if (MakeSymlink(args[1], args[2]) < 0) {
// The symlink builtin is often used to create symlinks for older devices to be backwards
// compatible with new paths, therefore we skip reporting this error.
if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
- return Success();
+ return {};
}
return ErrnoError() << "symlink() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_rm(const BuiltinArguments& args) {
+static Result<void> do_rm(const BuiltinArguments& args) {
if (unlink(args[1].c_str()) < 0) {
return ErrnoError() << "unlink() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_rmdir(const BuiltinArguments& args) {
+static Result<void> do_rmdir(const BuiltinArguments& args) {
if (rmdir(args[1].c_str()) < 0) {
return ErrnoError() << "rmdir() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_sysclktz(const BuiltinArguments& args) {
+static Result<void> do_sysclktz(const BuiltinArguments& args) {
struct timezone tz = {};
if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
return Error() << "Unable to parse mins_west_of_gmt";
@@ -718,42 +760,44 @@
if (settimeofday(nullptr, &tz) == -1) {
return ErrnoError() << "settimeofday() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
- int mode = -1;
- bool loaded = fs_mgr_load_verity_state(&mode);
- if (loaded && mode != VERITY_MODE_DEFAULT) {
- ActionManager::GetInstance().QueueEventTrigger("verity-logging");
+static Result<void> do_verity_update_state(const BuiltinArguments& args) {
+ int mode;
+ if (!fs_mgr_load_verity_state(&mode)) {
+ return Error() << "fs_mgr_load_verity_state() failed";
}
- if (!loaded) return Error() << "Could not load verity state";
- return Success();
-}
-
-static void verity_update_property(fstab_rec *fstab, const char *mount_point,
- int mode, int status) {
- property_set("partition."s + mount_point + ".verified", std::to_string(mode));
-}
-
-static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
- if (!fs_mgr_update_verity_state(verity_update_property)) {
- return Error() << "fs_mgr_update_verity_state() failed";
+ Fstab fstab;
+ if (!ReadDefaultFstab(&fstab)) {
+ return Error() << "Failed to read default fstab";
}
- return Success();
+
+ for (const auto& entry : fstab) {
+ if (!fs_mgr_is_verity_enabled(entry)) {
+ continue;
+ }
+
+ // To be consistent in vboot 1.0 and vboot 2.0 (AVB), use "system" for the partition even
+ // for system as root, so it has property [partition.system.verified].
+ std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
+ property_set("partition." + partition + ".verified", std::to_string(mode));
+ }
+
+ return {};
}
-static Result<Success> do_write(const BuiltinArguments& args) {
+static Result<void> do_write(const BuiltinArguments& args) {
if (auto result = WriteFile(args[1], args[2]); !result) {
return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> readahead_file(const std::string& filename, bool fully) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+static Result<void> readahead_file(const std::string& filename, bool fully) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
return ErrnoError() << "Error opening file";
}
@@ -772,10 +816,10 @@
return ErrnoError() << "Error reading file";
}
}
- return Success();
+ return {};
}
-static Result<Success> do_readahead(const BuiltinArguments& args) {
+static Result<void> do_readahead(const BuiltinArguments& args) {
struct stat sb;
if (stat(args[1].c_str(), &sb)) {
@@ -831,10 +875,10 @@
} else if (pid < 0) {
return ErrnoError() << "Fork failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_copy(const BuiltinArguments& args) {
+static Result<void> do_copy(const BuiltinArguments& args) {
auto file_contents = ReadFile(args[1]);
if (!file_contents) {
return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -843,10 +887,10 @@
return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
}
- return Success();
+ return {};
}
-static Result<Success> do_chown(const BuiltinArguments& args) {
+static Result<void> do_chown(const BuiltinArguments& args) {
auto uid = DecodeUid(args[1]);
if (!uid) {
return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -867,7 +911,7 @@
return ErrnoError() << "lchown() failed";
}
- return Success();
+ return {};
}
static mode_t get_mode(const char *s) {
@@ -883,63 +927,40 @@
return mode;
}
-static Result<Success> do_chmod(const BuiltinArguments& args) {
+static Result<void> do_chmod(const BuiltinArguments& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
return ErrnoError() << "fchmodat() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_restorecon(const BuiltinArguments& args) {
+static Result<void> do_restorecon(const BuiltinArguments& args) {
+ auto restorecon_info = ParseRestorecon(args.args);
+ if (!restorecon_info) {
+ return restorecon_info.error();
+ }
+
+ const auto& [flag, paths] = *restorecon_info;
+
int ret = 0;
-
- struct flag_type {const char* name; int value;};
- static const flag_type flags[] = {
- {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
- {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
- {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
- {0, 0}
- };
-
- int flag = 0;
-
- bool in_flags = true;
- for (size_t i = 1; i < args.size(); ++i) {
- if (android::base::StartsWith(args[i], "--")) {
- if (!in_flags) {
- return Error() << "flags must precede paths";
- }
- bool found = false;
- for (size_t j = 0; flags[j].name; ++j) {
- if (args[i] == flags[j].name) {
- flag |= flags[j].value;
- found = true;
- break;
- }
- }
- if (!found) {
- return Error() << "bad flag " << args[i];
- }
- } else {
- in_flags = false;
- if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
- ret = errno;
- }
+ for (const auto& path : paths) {
+ if (selinux_android_restorecon(path.c_str(), flag) < 0) {
+ ret = errno;
}
}
if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
- return Success();
+ return {};
}
-static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
std::vector<std::string> non_const_args(args.args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
return do_restorecon({std::move(non_const_args), args.context});
}
-static Result<Success> do_loglevel(const BuiltinArguments& args) {
+static Result<void> do_loglevel(const BuiltinArguments& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -957,20 +978,20 @@
return Error() << "invalid log level " << log_level;
}
android::base::SetMinimumLogSeverity(severity);
- return Success();
+ return {};
}
-static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
+static Result<void> do_load_persist_props(const BuiltinArguments& args) {
load_persist_props();
- return Success();
+ return {};
}
-static Result<Success> do_load_system_props(const BuiltinArguments& args) {
- load_system_props();
- return Success();
+static Result<void> do_load_system_props(const BuiltinArguments& args) {
+ LOG(INFO) << "deprecated action `load_system_props` called.";
+ return {};
}
-static Result<Success> do_wait(const BuiltinArguments& args) {
+static Result<void> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
int timeout_int;
@@ -984,10 +1005,10 @@
return Error() << "wait_for_file() failed";
}
- return Success();
+ return {};
}
-static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
+static Result<void> do_wait_for_prop(const BuiltinArguments& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
@@ -1001,22 +1022,23 @@
if (!start_waiting_for_property(name, value)) {
return Error() << "already waiting for a property";
}
- return Success();
+ return {};
}
static bool is_file_crypto() {
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
- const BuiltinArguments& args) {
+static Result<void> ExecWithRebootOnFailure(const std::string& reboot_reason,
+ const BuiltinArguments& args) {
auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
- return Error() << "Could not create exec service";
+ return Error() << "Could not create exec service: " << service.error();
}
- service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+ (*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- if (fscrypt_is_native()) {
+ // TODO (b/122850122): support this in gsi
+ if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
if (auto result = reboot_into_recovery(
{"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1028,15 +1050,15 @@
}
}
});
- if (auto result = service->ExecStart(); !result) {
+ if (auto result = (*service)->ExecStart(); !result) {
return Error() << "Could not start exec service: " << result.error();
}
- ServiceList::GetInstance().AddService(std::move(service));
- return Success();
+ ServiceList::GetInstance().AddService(std::move(*service));
+ return {};
}
-static Result<Success> do_installkey(const BuiltinArguments& args) {
- if (!is_file_crypto()) return Success();
+static Result<void> do_installkey(const BuiltinArguments& args) {
+ if (!is_file_crypto()) return {};
auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
@@ -1047,23 +1069,78 @@
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
}
-static Result<Success> do_init_user0(const BuiltinArguments& args) {
+static Result<void> do_init_user0(const BuiltinArguments& args) {
return ExecWithRebootOnFailure(
"init_user0_failed",
{{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context});
}
+static Result<void> do_mark_post_data(const BuiltinArguments& args) {
+ ServiceList::GetInstance().MarkPostData();
+
+ return {};
+}
+
+static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+ glob_t glob_result;
+ static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
+ const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
+ if (ret != 0 && ret != GLOB_NOMATCH) {
+ globfree(&glob_result);
+ return Error() << "glob pattern '" << glob_pattern << "' failed";
+ }
+ std::vector<std::string> configs;
+ Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance());
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ std::string path = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ std::vector<std::string> paths = android::base::Split(path, "/");
+ if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+ continue;
+ }
+ configs.push_back(path);
+ }
+ globfree(&glob_result);
+
+ bool success = true;
+ for (const auto& c : configs) {
+ if (c.back() == '/') {
+ // skip if directory
+ continue;
+ }
+ success &= parser.ParseConfigFile(c);
+ }
+ ServiceList::GetInstance().MarkServicesUpdate();
+ if (success) {
+ return {};
+ } else {
+ return Error() << "Could not parse apex configs";
+ }
+}
+
+static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
+ if (SwitchToDefaultMountNamespace()) {
+ return {};
+ } else {
+ return Error() << "Failed to enter into default mount namespace";
+ }
+}
+
// Builtin-function-map start
-const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap& GetBuiltinFunctionMap() {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
- static const Map builtin_functions = {
+ static const BuiltinFunctionMap builtin_functions = {
{"bootchart", {1, 1, {false, do_bootchart}}},
{"chmod", {2, 2, {true, do_chmod}}},
{"chown", {2, 3, {true, do_chown}}},
{"class_reset", {1, 1, {false, do_class_reset}}},
+ {"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}},
{"class_restart", {1, 1, {false, do_class_restart}}},
{"class_start", {1, 1, {false, do_class_start}}},
+ {"class_start_post_data", {1, 1, {false, do_class_start_post_data}}},
{"class_stop", {1, 1, {false, do_class_stop}}},
{"copy", {2, 2, {true, do_copy}}},
{"domainname", {1, 1, {true, do_domainname}}},
@@ -1083,6 +1160,7 @@
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
{"load_system_props", {0, 0, {false, do_load_system_props}}},
{"loglevel", {1, 1, {false, do_loglevel}}},
+ {"mark_post_data", {0, 0, {false, do_mark_post_data}}},
{"mkdir", {1, 4, {true, do_mkdir}}},
// TODO: Do mount operations in vendor_init.
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
@@ -1090,7 +1168,9 @@
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {1, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
+ {"parse_apex_configs", {0, 0, {false, do_parse_apex_configs}}},
{"umount", {1, 1, {false, do_umount}}},
+ {"umount_all", {1, 1, {false, do_umount_all}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"restart", {1, 1, {false, do_restart}}},
{"restorecon", {1, kMax, {true, do_restorecon}}},
@@ -1102,10 +1182,10 @@
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {1, 1, {false, do_swapon_all}}},
+ {"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}},
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
- {"verity_load_state", {0, 0, {false, do_verity_load_state}}},
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
{"wait", {1, 2, {true, do_wait}}},
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
diff --git a/init/builtins.h b/init/builtins.h
index 814b2d5..f0ff1eb 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_BUILTINS_H
-#define _INIT_BUILTINS_H
+#pragma once
#include <functional>
#include <map>
@@ -29,18 +28,18 @@
namespace android {
namespace init {
-using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
-using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
-class BuiltinFunctionMap : public KeywordFunctionMap {
- public:
- BuiltinFunctionMap() {}
-
- private:
- const Map& map() const override;
+struct BuiltinFunctionMapValue {
+ bool run_in_subcontext;
+ BuiltinFunction function;
};
+using BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>;
+
+const BuiltinFunctionMap& GetBuiltinFunctionMap();
+
+extern std::vector<std::string> late_import_paths;
+
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
new file mode 100644
index 0000000..3bd4774
--- /dev/null
+++ b/init/check_builtins.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Note that these check functions cannot check expanded arguments from properties, since they will
+// not know what those properties would be at runtime. They will be passed an empty string in the
+// situation that the input line had a property expansion without a default value, since an empty
+// string is otherwise an impossible value. They should therefore disregard checking empty
+// arguments.
+
+#include "check_builtins.h"
+
+#include <sys/time.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "builtin_arguments.h"
+#include "rlimit_parser.h"
+#include "service.h"
+#include "util.h"
+
+using android::base::ParseInt;
+using android::base::StartsWith;
+
+#define ReturnIfAnyArgsEmpty() \
+ for (const auto& arg : args) { \
+ if (arg.empty()) { \
+ return {}; \
+ } \
+ }
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args) {
+ if (!args[1].empty()) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
+ }
+ }
+
+ // GID is optional and pushes the index of path out by one if specified.
+ if (args.size() == 4 && !args[2].empty()) {
+ auto gid = DecodeUid(args[2]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
+ }
+ }
+
+ return {};
+}
+
+Result<void> check_exec(const BuiltinArguments& args) {
+ ReturnIfAnyArgsEmpty();
+
+ auto result = Service::MakeTemporaryOneshotService(args.args);
+ if (!result) {
+ return result.error();
+ }
+
+ return {};
+}
+
+Result<void> check_exec_background(const BuiltinArguments& args) {
+ return check_exec(std::move(args));
+}
+
+Result<void> check_load_system_props(const BuiltinArguments& args) {
+ return Error() << "'load_system_props' is deprecated";
+}
+
+Result<void> check_loglevel(const BuiltinArguments& args) {
+ ReturnIfAnyArgsEmpty();
+
+ int log_level = -1;
+ ParseInt(args[1], &log_level);
+ if (log_level < 0 || log_level > 7) {
+ return Error() << "loglevel must be in the range of 0-7";
+ }
+ return {};
+}
+
+Result<void> check_mkdir(const BuiltinArguments& args) {
+ if (args.size() >= 4) {
+ if (!args[3].empty()) {
+ auto uid = DecodeUid(args[3]);
+ if (!uid) {
+ return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+ }
+ }
+
+ if (args.size() == 5 && !args[4].empty()) {
+ auto gid = DecodeUid(args[4]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
+ }
+ }
+ }
+
+ return {};
+}
+
+Result<void> check_restorecon(const BuiltinArguments& args) {
+ ReturnIfAnyArgsEmpty();
+
+ auto restorecon_info = ParseRestorecon(args.args);
+ if (!restorecon_info) {
+ return restorecon_info.error();
+ }
+
+ return {};
+}
+
+Result<void> check_restorecon_recursive(const BuiltinArguments& args) {
+ return check_restorecon(std::move(args));
+}
+
+Result<void> check_setprop(const BuiltinArguments& args) {
+ const std::string& name = args[1];
+ if (name.empty()) {
+ return {};
+ }
+ const std::string& value = args[2];
+
+ if (!IsLegalPropertyName(name)) {
+ return Error() << "'" << name << "' is not a legal property name";
+ }
+
+ if (!value.empty()) {
+ if (auto result = IsLegalPropertyValue(name, value); !result) {
+ return result.error();
+ }
+ }
+
+ if (StartsWith(name, "ctl.")) {
+ return Error()
+ << "Do not set ctl. properties from init; call the Service functions directly";
+ }
+
+ static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+ if (name == kRestoreconProperty) {
+ return Error() << "Do not set '" << kRestoreconProperty
+ << "' from init; use the restorecon builtin directly";
+ }
+
+ return {};
+}
+
+Result<void> check_setrlimit(const BuiltinArguments& args) {
+ ReturnIfAnyArgsEmpty();
+
+ auto rlimit = ParseRlimit(args.args);
+ if (!rlimit) return rlimit.error();
+ return {};
+}
+
+Result<void> check_sysclktz(const BuiltinArguments& args) {
+ ReturnIfAnyArgsEmpty();
+
+ struct timezone tz = {};
+ if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+ return Error() << "Unable to parse mins_west_of_gmt";
+ }
+ return {};
+}
+
+Result<void> check_wait(const BuiltinArguments& args) {
+ if (args.size() == 3 && !args[2].empty()) {
+ int timeout_int;
+ if (!android::base::ParseInt(args[2], &timeout_int)) {
+ return Error() << "failed to parse timeout";
+ }
+ }
+ return {};
+}
+
+Result<void> check_wait_for_prop(const BuiltinArguments& args) {
+ return check_setprop(std::move(args));
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/check_builtins.h b/init/check_builtins.h
new file mode 100644
index 0000000..c974e88
--- /dev/null
+++ b/init/check_builtins.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args);
+Result<void> check_exec(const BuiltinArguments& args);
+Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_load_system_props(const BuiltinArguments& args);
+Result<void> check_loglevel(const BuiltinArguments& args);
+Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_restorecon(const BuiltinArguments& args);
+Result<void> check_restorecon_recursive(const BuiltinArguments& args);
+Result<void> check_setprop(const BuiltinArguments& args);
+Result<void> check_setrlimit(const BuiltinArguments& args);
+Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_wait(const BuiltinArguments& args);
+Result<void> check_wait_for_prop(const BuiltinArguments& args);
+
+} // namespace init
+} // namespace android
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/init/debug_ramdisk.h
similarity index 63%
copy from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
copy to init/debug_ramdisk.h
index 5d0d924..4e3a395 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/init/debug_ramdisk.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+#pragma once
-#include <stdint.h>
-#include <string>
+namespace android {
+namespace init {
-namespace unwindstack {
-class Memory;
-}
+constexpr const char kDebugRamdiskProp[] = "/debug_ramdisk/adb_debug.prop";
+constexpr const char kDebugRamdiskSEPolicy[] = "/debug_ramdisk/userdebug_plat_sepolicy.cil";
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
+} // namespace init
+} // namespace android
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
deleted file mode 100644
index 6265687..0000000
--- a/init/descriptors.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "descriptors.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
-
-#include "util.h"
-
-namespace android {
-namespace init {
-
-DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
-}
-
-DescriptorInfo::~DescriptorInfo() {
-}
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
- return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
-}
-
-bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
- return name_ == other.name_ && type_ == other.type_ && key() == other.key();
-}
-
-void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
- // Create
- const std::string& contextStr = context_.empty() ? globalContext : context_;
- int fd = Create(contextStr);
- if (fd < 0) return;
-
- // Publish
- std::string publishedName = key() + name_;
- std::for_each(publishedName.begin(), publishedName.end(),
- [] (char& c) { c = isalnum(c) ? c : '_'; });
-
- std::string val = std::to_string(fd);
- setenv(publishedName.c_str(), val.c_str(), 1);
-
- // make sure we don't close on exec
- fcntl(fd, F_SETFD, 0);
-}
-
-void DescriptorInfo::Clean() const {
-}
-
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-void SocketInfo::Clean() const {
- std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
- unlink(path.c_str());
-}
-
-int SocketInfo::Create(const std::string& context) const {
- auto types = android::base::Split(type(), "+");
- int flags =
- ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
- bool passcred = types.size() > 1 && types[1] == "passcred";
- return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
-}
-
-const std::string SocketInfo::key() const {
- return ANDROID_SOCKET_ENV_PREFIX;
-}
-
-FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context)
- // defaults OK for uid,..., they are ignored for this class.
- : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-int FileInfo::Create(const std::string&) const {
- int flags = (type() == "r") ? O_RDONLY :
- (type() == "w") ? O_WRONLY :
- O_RDWR;
-
- // Make sure we do not block on open (eg: devices can chose to block on
- // carrier detect). Our intention is never to delay launch of a service
- // for such a condition. The service can perform its own blocking on
- // carrier detect.
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
- flags | O_NONBLOCK)));
-
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
- return -1;
- }
-
- // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
- fcntl(fd, F_SETFL, flags);
-
- LOG(INFO) << "Opened file '" << name().c_str() << "'"
- << ", flags " << std::oct << flags << std::dec;
-
- return fd.release();
-}
-
-const std::string FileInfo::key() const {
- return ANDROID_FILE_ENV_PREFIX;
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
deleted file mode 100644
index 3bdddfe..0000000
--- a/init/descriptors.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef _INIT_DESCRIPTORS_H
-#define _INIT_DESCRIPTORS_H
-
-#include <sys/types.h>
-
-#include <string>
-
-namespace android {
-namespace init {
-
-class DescriptorInfo {
- public:
- DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- virtual ~DescriptorInfo();
-
- friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
- bool operator==(const DescriptorInfo& other) const;
-
- void CreateAndPublish(const std::string& globalContext) const;
- virtual void Clean() const;
-
- protected:
- const std::string& name() const { return name_; }
- const std::string& type() const { return type_; }
- uid_t uid() const { return uid_; }
- gid_t gid() const { return gid_; }
- int perm() const { return perm_; }
- const std::string& context() const { return context_; }
-
- private:
- std::string name_;
- std::string type_;
- uid_t uid_;
- gid_t gid_;
- int perm_;
- std::string context_;
-
- virtual int Create(const std::string& globalContext) const = 0;
- virtual const std::string key() const = 0;
-};
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
-
-class SocketInfo : public DescriptorInfo {
- public:
- SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- void Clean() const override;
- private:
- virtual int Create(const std::string& context) const override;
- virtual const std::string key() const override;
-};
-
-class FileInfo : public DescriptorInfo {
- public:
- FileInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& context);
- private:
- virtual int Create(const std::string& context) const override;
- virtual const std::string key() const override;
-};
-
-} // namespace init
-} // namespace android
-
-#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 58c8b2e..f6e453a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -21,8 +21,13 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
+#include <string>
+#include <thread>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -30,19 +35,19 @@
#include <selinux/android.h>
#include <selinux/selinux.h>
-#include "selinux.h"
+#include "selabel.h"
#include "util.h"
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd; it will expose init's globals"
-#endif
+using namespace std::chrono_literals;
using android::base::Basename;
using android::base::Dirname;
+using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Realpath;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::Trim;
namespace android {
namespace init {
@@ -101,6 +106,23 @@
return true;
}
+// Given a path that may start with a virtual dm block device, populate
+// the supplied buffer with the dm module's instantiated name.
+// If it doesn't start with a virtual block device, or there is some
+// error, return false.
+static bool FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
+ if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+
+ if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
+ return false;
+ }
+ ReadFileToString("/sys" + path + "/dm/uuid", uuid);
+
+ *name = android::base::Trim(*name);
+ *uuid = android::base::Trim(*uuid);
+ return true;
+}
+
Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
: name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
// Set 'prefix_' or 'wildcard_' based on the below cases:
@@ -293,6 +315,8 @@
std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
std::string device;
std::string type;
+ std::string partition;
+ std::string uuid;
if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
@@ -310,6 +334,12 @@
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
+ } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
+ std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
+ if (!uuid.empty()) {
+ symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
+ }
+ return symlinks;
} else {
return {};
}
@@ -320,6 +350,7 @@
auto link_path = "/dev/block/" + type + "/" + device;
+ bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
if (!uevent.partition_name.empty()) {
std::string partition_name_sanitized(uevent.partition_name);
SanitizePartitionName(&partition_name_sanitized);
@@ -329,9 +360,13 @@
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
// Adds symlink: /dev/block/by-name/<partition_name>.
- if (boot_devices_.find(device) != boot_devices_.end()) {
+ if (is_boot_device) {
links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
}
+ } else if (is_boot_device) {
+ // If we don't have a partition name but we are a partition on a boot device, create a
+ // symlink of /dev/block/by-name/<device_name> for symmetry.
+ links.emplace_back("/dev/block/by-name/" + uevent.device_name);
}
auto last_slash = uevent.path.rfind('/');
@@ -340,10 +375,41 @@
return links;
}
+static void RemoveDeviceMapperLinks(const std::string& devpath) {
+ std::vector<std::string> dirs = {
+ "/dev/block/mapper",
+ "/dev/block/mapper/by-uuid",
+ };
+ for (const auto& dir : dirs) {
+ if (access(dir.c_str(), F_OK) != 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
+ if (!dh) {
+ PLOG(ERROR) << "Failed to open directory " << dir;
+ continue;
+ }
+
+ struct dirent* dp;
+ std::string link_path;
+ while ((dp = readdir(dh.get())) != nullptr) {
+ if (dp->d_type != DT_LNK) continue;
+
+ auto path = dir + "/" + dp->d_name;
+ if (Readlink(path, &link_path) && link_path == devpath) {
+ unlink(path.c_str());
+ }
+ }
+ }
+}
+
void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
+ }
+
+ // We don't have full device-mapper information until a change event is fired.
+ if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
for (const auto& link : links) {
if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
@@ -362,6 +428,9 @@
}
if (action == "remove") {
+ if (StartsWith(devpath, "/dev/block/dm-")) {
+ RemoveDeviceMapperLinks(devpath);
+ }
for (const auto& link : links) {
std::string link_path;
if (Readlink(link, &link_path) && link_path == devpath) {
@@ -419,7 +488,7 @@
}
void DeviceHandler::ColdbootDone() {
- skip_restorecon_ = true;
+ skip_restorecon_ = false;
}
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/devices.h b/init/devices.h
index 9d39eaa..442c53f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -84,9 +84,9 @@
};
Subsystem() {}
- Subsystem(const std::string& name) : name_(name) {}
- Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
- : name_(name), devname_source_(source), dir_name_(dir_name) {}
+ Subsystem(std::string name) : name_(std::move(name)) {}
+ Subsystem(std::string name, DevnameSource source, std::string dir_name)
+ : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_name)) {}
// Returns the full path for a uevent of a device that is a member of this subsystem,
// according to the rules parsed from ueventd.rc
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index d658f4d..c408bc1 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -16,8 +16,8 @@
#include "devices.h"
+#include <android-base/file.h>
#include <android-base/scopeguard.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "util.h"
@@ -30,7 +30,7 @@
class DeviceHandlerTester {
public:
void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
- const std::vector<std::string> expected_links) {
+ const std::vector<std::string>& expected_links) {
TemporaryDir fake_sys_root;
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 4bca09e..01d8867 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -16,6 +16,7 @@
#include "epoll.h"
+#include <stdint.h>
#include <sys/epoll.h>
#include <chrono>
@@ -27,45 +28,48 @@
Epoll::Epoll() {}
-Result<Success> Epoll::Open() {
- if (epoll_fd_ >= 0) return Success();
+Result<void> Epoll::Open() {
+ if (epoll_fd_ >= 0) return {};
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd_ == -1) {
return ErrnoError() << "epoll_create1 failed";
}
- return Success();
+ return {};
}
-Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+ if (!events) {
+ return Error() << "Must specify events";
+ }
auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(handler));
if (!inserted) {
return Error() << "Cannot specify two epoll handlers for a given FD";
}
epoll_event ev;
- ev.events = EPOLLIN;
+ ev.events = events;
// std::map's iterators do not get invalidated until erased, so we use the
// pointer to the std::function in the map directly for epoll_ctl.
ev.data.ptr = reinterpret_cast<void*>(&it->second);
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
- Result<Success> result = ErrnoError() << "epoll_ctl failed to add fd";
+ Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
epoll_handlers_.erase(fd);
return result;
}
- return Success();
+ return {};
}
-Result<Success> Epoll::UnregisterHandler(int fd) {
+Result<void> Epoll::UnregisterHandler(int fd) {
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
return ErrnoError() << "epoll_ctl failed to remove fd";
}
if (epoll_handlers_.erase(fd) != 1) {
return Error() << "Attempting to remove epoll handler for FD without an existing handler";
}
- return Success();
+ return {};
}
-Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+Result<void> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
int timeout_ms = -1;
if (timeout && timeout->count() < INT_MAX) {
timeout_ms = timeout->count();
@@ -77,7 +81,7 @@
} else if (nr == 1) {
std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index 85a791c..ca84266 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -17,6 +17,9 @@
#ifndef _INIT_EPOLL_H
#define _INIT_EPOLL_H
+#include <stdint.h>
+#include <sys/epoll.h>
+
#include <chrono>
#include <functional>
#include <map>
@@ -33,10 +36,10 @@
public:
Epoll();
- Result<Success> Open();
- Result<Success> RegisterHandler(int fd, std::function<void()> handler);
- Result<Success> UnregisterHandler(int fd);
- Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
+ Result<void> Open();
+ Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
+ Result<void> UnregisterHandler(int fd);
+ Result<void> Wait(std::optional<std::chrono::milliseconds> timeout);
private:
android::base::unique_fd epoll_fd_;
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 740e82c..c067f6f 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -80,15 +80,26 @@
return;
}
+ std::vector<std::string> attempted_paths_and_errors;
+
try_loading_again:
+ attempted_paths_and_errors.clear();
for (const auto& firmware_directory : firmware_directories_) {
std::string file = firmware_directory + uevent.firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
- struct stat sb;
- if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
- LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
- return;
+ if (fw_fd == -1) {
+ attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+ ", open failed: " + strerror(errno));
+ continue;
}
+ struct stat sb;
+ if (fstat(fw_fd, &sb) == -1) {
+ attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
+ ", fstat failed: " + strerror(errno));
+ continue;
+ }
+ LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return;
}
if (booting) {
@@ -100,6 +111,9 @@
}
LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+ for (const auto& message : attempted_paths_and_errors) {
+ LOG(ERROR) << message;
+ }
// Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
write(loading_fd, "-1", 2);
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
new file mode 100644
index 0000000..b60c450
--- /dev/null
+++ b/init/first_stage_init.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "first_stage_init.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <modprobe/modprobe.h>
+#include <private/android_filesystem_config.h>
+
+#include "debug_ramdisk.h"
+#include "first_stage_mount.h"
+#include "reboot_utils.h"
+#include "switch_root.h"
+#include "util.h"
+
+using android::base::boot_clock;
+
+using namespace std::literals;
+
+namespace fs = std::filesystem;
+
+namespace android {
+namespace init {
+
+namespace {
+
+void FreeRamdisk(DIR* dir, dev_t dev) {
+ int dfd = dirfd(dir);
+
+ dirent* de;
+ while ((de = readdir(dir)) != nullptr) {
+ if (de->d_name == "."s || de->d_name == ".."s) {
+ continue;
+ }
+
+ bool is_dir = false;
+
+ if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
+ struct stat info;
+ if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
+ continue;
+ }
+
+ if (info.st_dev != dev) {
+ continue;
+ }
+
+ if (S_ISDIR(info.st_mode)) {
+ is_dir = true;
+ auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (fd >= 0) {
+ auto subdir =
+ std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
+ if (subdir) {
+ FreeRamdisk(subdir.get(), dev);
+ } else {
+ close(fd);
+ }
+ }
+ }
+ }
+ unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
+ }
+}
+
+void StartConsole() {
+ if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+ PLOG(ERROR) << "unable to create /dev/console";
+ return;
+ }
+ pid_t pid = fork();
+ if (pid != 0) {
+ int status;
+ waitpid(pid, &status, 0);
+ LOG(ERROR) << "console shell exited with status " << status;
+ return;
+ }
+ int fd = -1;
+ int tries = 10;
+ // The device driver for console may not be ready yet so retry for a while in case of failure.
+ while (tries--) {
+ fd = open("/dev/console", O_RDWR);
+ if (fd != -1) {
+ break;
+ }
+ std::this_thread::sleep_for(100ms);
+ }
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+ _exit(127);
+ }
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+
+ const char* path = "/system/bin/sh";
+ const char* args[] = {path, nullptr};
+ int rv = execv(path, const_cast<char**>(args));
+ LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+ _exit(127);
+}
+
+bool FirstStageConsole(const std::string& cmdline) {
+ return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+}
+
+bool ForceNormalBoot(const std::string& cmdline) {
+ return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+}
+
+} // namespace
+
+int FirstStageMain(int argc, char** argv) {
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ boot_clock::time_point start_time = boot_clock::now();
+
+ std::vector<std::pair<std::string, int>> errors;
+#define CHECKCALL(x) \
+ if ((x) != 0) errors.emplace_back(#x " failed", errno);
+
+ // Clear the umask.
+ umask(0);
+
+ CHECKCALL(clearenv());
+ CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
+ // Get the basic filesystem setup we need put together in the initramdisk
+ // on / and then we'll let the rc file figure out the rest.
+ CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
+ CHECKCALL(mkdir("/dev/pts", 0755));
+ CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
+#define MAKE_STR(x) __STRING(x)
+ CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
+#undef MAKE_STR
+ // Don't expose the raw commandline to unprivileged processes.
+ CHECKCALL(chmod("/proc/cmdline", 0440));
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ gid_t groups[] = {AID_READPROC};
+ CHECKCALL(setgroups(arraysize(groups), groups));
+ CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
+ CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
+
+ CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
+
+ if constexpr (WORLD_WRITABLE_KMSG) {
+ CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
+ }
+
+ CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
+ CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
+
+ // This is needed for log wrapper, which gets called before ueventd runs.
+ CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
+ CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
+
+ // These below mounts are done in first stage init so that first stage mount can mount
+ // subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
+ // should be done in rc files.
+ // Mount staging areas for devices managed by vold
+ // See storage config details at http://source.android.com/devices/storage/
+ CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=1000"));
+ // /mnt/vendor is used to mount vendor-specific partitions that can not be
+ // part of the vendor partition, e.g. because they are mounted read-write.
+ CHECKCALL(mkdir("/mnt/vendor", 0755));
+ // /mnt/product is used to mount product-specific partitions that can not be
+ // part of the product partition, e.g. because they are mounted read-write.
+ CHECKCALL(mkdir("/mnt/product", 0755));
+
+ // /apex is used to mount APEXes
+ CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+
+ // /debug_ramdisk is used to preserve additional files from the debug ramdisk
+ CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+#undef CHECKCALL
+
+ SetStdioToDevNull(argv);
+ // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
+ // talk to the outside world...
+ InitKernelLogging(argv);
+
+ if (!errors.empty()) {
+ for (const auto& [error_string, error_errno] : errors) {
+ LOG(ERROR) << error_string << " " << strerror(error_errno);
+ }
+ LOG(FATAL) << "Init encountered errors starting first stage, aborting";
+ }
+
+ LOG(INFO) << "init first stage started!";
+
+ auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
+ if (!old_root_dir) {
+ PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
+ }
+
+ struct stat old_root_info;
+ if (stat("/", &old_root_info) != 0) {
+ PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+ old_root_dir.reset();
+ }
+
+ Modprobe m({"/lib/modules"});
+ if (!m.LoadListedModules()) {
+ LOG(FATAL) << "Failed to load kernel modules";
+ }
+
+ if (ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline)) {
+ StartConsole();
+ }
+
+ if (ForceNormalBoot(cmdline)) {
+ mkdir("/first_stage_ramdisk", 0755);
+ // SwitchRoot() must be called with a mount point as the target, so we bind mount the
+ // target directory to itself here.
+ if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
+ LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+ }
+ SwitchRoot("/first_stage_ramdisk");
+ }
+
+ // If this file is present, the second-stage init will use a userdebug sepolicy
+ // and load adb_debug.prop to allow adb root, if the device is unlocked.
+ if (access("/force_debuggable", F_OK) == 0) {
+ std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
+ if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
+ !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
+ LOG(ERROR) << "Failed to setup debug ramdisk";
+ } else {
+ // setenv for second-stage init to read above kDebugRamdisk* files.
+ setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
+ }
+ }
+
+ if (!DoFirstStageMount()) {
+ LOG(FATAL) << "Failed to mount required partitions early ...";
+ }
+
+ struct stat new_root_info;
+ if (stat("/", &new_root_info) != 0) {
+ PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+ old_root_dir.reset();
+ }
+
+ if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
+ FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
+ }
+
+ SetInitAvbVersionInRecovery();
+
+ setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
+ 1);
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "selinux_setup", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never fall through this conditional.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}
+
+} // namespace init
+} // namespace android
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/init/first_stage_init.h
similarity index 73%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to init/first_stage_init.h
index 410d379..7de816f 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/init/first_stage_init.h
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+namespace android {
+namespace init {
+
+int FirstStageMain(int argc, char** argv);
+
+static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
+
+} // namespace init
+} // namespace android
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/init/first_stage_main.cpp
similarity index 77%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to init/first_stage_main.cpp
index 410d379..7bae84c 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/init/first_stage_main.cpp
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#include "first_stage_init.h"
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
+int main(int argc, char** argv) {
+ return android::init::FirstStageMain(argc, argv);
}
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index eb86eb0..1a5ed28 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -17,6 +17,7 @@
#include "first_stage_mount.h"
#include <stdlib.h>
+#include <sys/mount.h>
#include <unistd.h>
#include <chrono>
@@ -29,11 +30,12 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
+#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
-#include <fs_mgr_avb.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
-#include <liblp/metadata_format.h>
+#include <libgsi/libgsi.h>
+#include <liblp/liblp.h>
#include "devices.h"
#include "switch_root.h"
@@ -41,7 +43,18 @@
#include "uevent_listener.h"
#include "util.h"
+using android::base::Split;
using android::base::Timer;
+using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
+using android::fs_mgr::AvbHashtreeResult;
+using android::fs_mgr::AvbUniquePtr;
+using android::fs_mgr::BuildGsiSystemFstabEntry;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::ReadDefaultFstab;
+using android::fs_mgr::ReadFstabFromDt;
+using android::fs_mgr::SkipMountingPartitions;
using namespace std::literals;
@@ -52,7 +65,7 @@
// ------------------
class FirstStageMount {
public:
- FirstStageMount();
+ FirstStageMount(Fstab fstab);
virtual ~FirstStageMount() = default;
// The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
@@ -65,23 +78,30 @@
ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
bool InitRequiredDevices();
bool InitMappedDevice(const std::string& verity_device);
+ bool InitDeviceMapper();
bool CreateLogicalPartitions();
- bool MountPartition(fstab_rec* fstab_rec);
- bool MountPartitions();
- bool IsDmLinearEnabled();
- bool GetBackingDmLinearDevices();
+ bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+ Fstab::iterator* end = nullptr);
- virtual ListenerAction UeventCallback(const Uevent& uevent);
+ bool MountPartitions();
+ bool TrySwitchSystemAsRoot();
+ bool TrySkipMountingPartitions();
+ bool IsDmLinearEnabled();
+ bool GetDmLinearMetadataDevice();
+ bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
+ void UseGsiIfPresent();
+
+ ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions.
virtual bool GetDmVerityDevices() = 0;
- virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+ virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
bool need_dm_verity_;
+ bool gsi_not_on_userdata_ = false;
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+ Fstab fstab_;
std::string lp_metadata_partition_;
- std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
std::string super_partition_name_;
std::unique_ptr<DeviceHandler> device_handler_;
@@ -90,67 +110,115 @@
class FirstStageMountVBootV1 : public FirstStageMount {
public:
- FirstStageMountVBootV1() = default;
+ FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {}
~FirstStageMountVBootV1() override = default;
protected:
bool GetDmVerityDevices() override;
- bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool SetUpDmVerity(FstabEntry* fstab_entry) override;
};
class FirstStageMountVBootV2 : public FirstStageMount {
public:
friend void SetInitAvbVersionInRecovery();
- FirstStageMountVBootV2();
+ FirstStageMountVBootV2(Fstab fstab);
~FirstStageMountVBootV2() override = default;
protected:
- ListenerAction UeventCallback(const Uevent& uevent) override;
bool GetDmVerityDevices() override;
- bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool SetUpDmVerity(FstabEntry* fstab_entry) override;
bool InitAvbHandle();
- std::string device_tree_vbmeta_parts_;
- FsManagerAvbUniquePtr avb_handle_;
- ByNameSymlinkMap by_name_symlink_map_;
+ std::vector<std::string> vbmeta_partitions_;
+ AvbUniquePtr avb_handle_;
};
// Static Functions
// ----------------
-static inline bool IsDtVbmetaCompatible() {
+static inline bool IsDtVbmetaCompatible(const Fstab& fstab) {
+ if (std::any_of(fstab.begin(), fstab.end(),
+ [](const auto& entry) { return entry.fs_mgr_flags.avb; })) {
+ return true;
+ }
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static bool ForceNormalBoot() {
- static bool force_normal_boot = []() {
- std::string cmdline;
- android::base::ReadFileToString("/proc/cmdline", &cmdline);
- return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
- }();
-
- return force_normal_boot;
+static Fstab ReadFirstStageFstab() {
+ Fstab fstab;
+ if (!ReadFstabFromDt(&fstab)) {
+ if (ReadDefaultFstab(&fstab)) {
+ fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
+ [](const auto& entry) {
+ return !entry.fs_mgr_flags.first_stage_mount;
+ }),
+ fstab.end());
+ } else {
+ LOG(INFO) << "Failed to fstab for first stage mount";
+ }
+ }
+ return fstab;
}
-static bool IsRecoveryMode() {
- return !ForceNormalBoot() && access("/system/bin/recovery", F_OK) == 0;
+static bool GetRootEntry(FstabEntry* root_entry) {
+ Fstab proc_mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+ LOG(ERROR) << "Could not read /proc/mounts and /system not in fstab, /system will not be "
+ "available for overlayfs";
+ return false;
+ }
+
+ auto entry = std::find_if(proc_mounts.begin(), proc_mounts.end(), [](const auto& entry) {
+ return entry.mount_point == "/" && entry.fs_type != "rootfs";
+ });
+
+ if (entry == proc_mounts.end()) {
+ LOG(ERROR) << "Could not get mount point for '/' in /proc/mounts, /system will not be "
+ "available for overlayfs";
+ return false;
+ }
+
+ *root_entry = std::move(*entry);
+
+ // We don't know if we're avb or not, so we query device mapper as if we are avb. If we get a
+ // success, then mark as avb, otherwise default to verify.
+ auto& dm = android::dm::DeviceMapper::Instance();
+ if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) {
+ root_entry->fs_mgr_flags.avb = true;
+ } else {
+ root_entry->fs_mgr_flags.verify = true;
+ }
+ return true;
+}
+
+static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
+ const AvbHandle& standalone_vbmeta,
+ const FstabEntry& fstab_entry) {
+ std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);
+ std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);
+
+ bool rollbacked = false;
+ if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {
+ rollbacked = true;
+ }
+
+ if (rollbacked) {
+ LOG(ERROR) << "Image rollback detected for " << fstab_entry.mount_point
+ << ", SPL switches from '" << old_spl << "' to '" << new_spl << "'";
+ if (AvbHandle::IsDeviceUnlocked()) {
+ LOG(INFO) << "Allowing rollbacked standalone image when the device is unlocked";
+ return false;
+ }
+ }
+
+ return rollbacked;
}
// Class Definitions
// -----------------
-FirstStageMount::FirstStageMount()
- : need_dm_verity_(false), device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
- if (device_tree_fstab_) {
- // Stores device_tree_fstab_->recs[] into mount_fstab_recs_ (vector<fstab_rec*>)
- // for easier manipulation later, e.g., range-base for loop.
- for (int i = 0; i < device_tree_fstab_->num_entries; i++) {
- mount_fstab_recs_.push_back(&device_tree_fstab_->recs[i]);
- }
- } else {
- LOG(INFO) << "Failed to read fstab from device tree";
- }
-
- auto boot_devices = fs_mgr_get_boot_devices();
+FirstStageMount::FirstStageMount(Fstab fstab)
+ : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
+ auto boot_devices = android::fs_mgr::GetBootDevices();
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
@@ -159,15 +227,16 @@
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
- if (IsDtVbmetaCompatible()) {
- return std::make_unique<FirstStageMountVBootV2>();
+ auto fstab = ReadFirstStageFstab();
+ if (IsDtVbmetaCompatible(fstab)) {
+ return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
} else {
- return std::make_unique<FirstStageMountVBootV1>();
+ return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
}
}
bool FirstStageMount::DoFirstStageMount() {
- if (!IsDmLinearEnabled() && mount_fstab_recs_.empty()) {
+ if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
@@ -183,17 +252,17 @@
}
bool FirstStageMount::InitDevices() {
- return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+ return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
}
bool FirstStageMount::IsDmLinearEnabled() {
- for (auto fstab_rec : mount_fstab_recs_) {
- if (fs_mgr_is_logical(fstab_rec)) return true;
+ for (const auto& entry : fstab_) {
+ if (entry.fs_mgr_flags.logical) return true;
}
return false;
}
-bool FirstStageMount::GetBackingDmLinearDevices() {
+bool FirstStageMount::GetDmLinearMetadataDevice() {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return true;
@@ -207,32 +276,12 @@
// required_devices_partition_names_. Found partitions will then be removed from it
// for the subsequent member function to check which devices are NOT created.
bool FirstStageMount::InitRequiredDevices() {
- if (required_devices_partition_names_.empty()) {
- return true;
+ if (!InitDeviceMapper()) {
+ return false;
}
- if (IsDmLinearEnabled() || need_dm_verity_) {
- const std::string dm_path = "/devices/virtual/misc/device-mapper";
- bool found = false;
- auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
- if (uevent.path == dm_path) {
- device_handler_->HandleUevent(uevent);
- found = true;
- return ListenerAction::kStop;
- }
- return ListenerAction::kContinue;
- };
- uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
- if (!found) {
- LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
- Timer t;
- uevent_listener_.Poll(dm_callback, 10s);
- LOG(INFO) << "Wait for device-mapper returned after " << t;
- }
- if (!found) {
- LOG(ERROR) << "device-mapper device not found after polling timeout";
- return false;
- }
+ if (required_devices_partition_names_.empty()) {
+ return true;
}
auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
@@ -258,17 +307,74 @@
return true;
}
+bool FirstStageMount::InitDeviceMapper() {
+ const std::string dm_path = "/devices/virtual/misc/device-mapper";
+ bool found = false;
+ auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
+ if (uevent.path == dm_path) {
+ device_handler_->HandleUevent(uevent);
+ found = true;
+ return ListenerAction::kStop;
+ }
+ return ListenerAction::kContinue;
+ };
+ uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
+ if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
+ uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
+ }
+ if (!found) {
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
+ return false;
+ }
+ return true;
+}
+
+bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+ auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
+ for (const auto& partition_name : partition_names) {
+ // The super partition was found in the earlier pass.
+ if (partition_name == super_partition_name_) {
+ continue;
+ }
+ required_devices_partition_names_.emplace(partition_name);
+ }
+ if (required_devices_partition_names_.empty()) {
+ return true;
+ }
+
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
+
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMount::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
-
if (lp_metadata_partition_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
}
- return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
+
+ auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+ return false;
+ }
+ if (!InitDmLinearBackingDevices(*metadata.get())) {
+ return false;
+ }
+ return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
@@ -329,133 +435,245 @@
uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
if (!found) {
- LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+ LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(verity_callback, 10s);
- LOG(INFO) << "wait for dm-verity device returned after " << t;
+ LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm-verity device not found after polling timeout";
+ LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
return false;
}
return true;
}
-bool FirstStageMount::MountPartition(fstab_rec* fstab_rec) {
- if (fs_mgr_is_logical(fstab_rec)) {
- if (!fs_mgr_update_logical_partition(fstab_rec)) {
+bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+ Fstab::iterator* end) {
+ // Sets end to begin + 1, so we can just return on failure below.
+ if (end) {
+ *end = begin + 1;
+ }
+
+ if (begin->fs_mgr_flags.logical) {
+ if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
}
- if (!InitMappedDevice(fstab_rec->blk_device)) {
+ if (!InitMappedDevice(begin->blk_device)) {
return false;
}
}
- if (!SetUpDmVerity(fstab_rec)) {
- PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+ if (!SetUpDmVerity(&(*begin))) {
+ PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
return false;
}
- if (fs_mgr_do_mount_one(fstab_rec)) {
- PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+
+ bool mounted = (fs_mgr_do_mount_one(*begin) == 0);
+
+ // Try other mounts with the same mount point.
+ Fstab::iterator current = begin + 1;
+ for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {
+ if (!mounted) {
+ // blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.
+ // Copy it from the begin iterator.
+ current->blk_device = begin->blk_device;
+ mounted = (fs_mgr_do_mount_one(*current) == 0);
+ }
+ }
+ if (erase_same_mounts) {
+ current = fstab_.erase(begin, current);
+ }
+ if (end) {
+ *end = current;
+ }
+ return mounted;
+}
+
+// If system is in the fstab then we're not a system-as-root device, and in
+// this case, we mount system first then pivot to it. From that point on,
+// we are effectively identical to a system-as-root device.
+bool FirstStageMount::TrySwitchSystemAsRoot() {
+ auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+ return entry.mount_point == "/metadata";
+ });
+ if (metadata_partition != fstab_.end()) {
+ if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
+ UseGsiIfPresent();
+ }
+ }
+
+ auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+ return entry.mount_point == "/system";
+ });
+
+ if (system_partition == fstab_.end()) return true;
+
+ if (MountPartition(system_partition, false /* erase_same_mounts */)) {
+ if (gsi_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+ LOG(ERROR) << "check_most_at_once forbidden on external media";
+ return false;
+ }
+ SwitchRoot("/system");
+ } else {
+ PLOG(ERROR) << "Failed to mount /system";
return false;
}
+
return true;
}
bool FirstStageMount::MountPartitions() {
- // If system is in the fstab then we're not a system-as-root device, and in
- // this case, we mount system first then pivot to it. From that point on,
- // we are effectively identical to a system-as-root device.
- auto system_partition =
- std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(),
- [](const auto& rec) { return rec->mount_point == "/system"s; });
+ if (!TrySwitchSystemAsRoot()) return false;
- if (system_partition != mount_fstab_recs_.end()) {
- if (ForceNormalBoot()) {
- free((*system_partition)->mount_point);
- (*system_partition)->mount_point = strdup("/system_recovery_mount");
+ if (!SkipMountingPartitions(&fstab_)) return false;
+
+ for (auto current = fstab_.begin(); current != fstab_.end();) {
+ // We've already mounted /system above.
+ if (current->mount_point == "/system") {
+ ++current;
+ continue;
}
- if (!MountPartition(*system_partition)) {
- return false;
+ Fstab::iterator end;
+ if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
+ if (current->fs_mgr_flags.no_fail) {
+ LOG(INFO) << "Failed to mount " << current->mount_point
+ << ", ignoring mount for no_fail partition";
+ } else if (current->fs_mgr_flags.formattable) {
+ LOG(INFO) << "Failed to mount " << current->mount_point
+ << ", ignoring mount for formattable partition";
+ } else {
+ PLOG(ERROR) << "Failed to mount " << current->mount_point;
+ return false;
+ }
}
-
- SwitchRoot((*system_partition)->mount_point);
-
- mount_fstab_recs_.erase(system_partition);
+ current = end;
}
- for (auto fstab_rec : mount_fstab_recs_) {
- if (!MountPartition(fstab_rec) && !fs_mgr_is_nofail(fstab_rec)) {
- return false;
+ // If we don't see /system or / in the fstab, then we need to create an root entry for
+ // overlayfs.
+ if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
+ FstabEntry root_entry;
+ if (GetRootEntry(&root_entry)) {
+ fstab_.emplace_back(std::move(root_entry));
}
}
// heads up for instantiating required device(s) for overlayfs logic
- const auto devices = fs_mgr_overlayfs_required_devices(device_tree_fstab_.get());
+ const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
for (auto const& device : devices) {
- InitMappedDevice(device);
+ if (android::base::StartsWith(device, "/dev/block/by-name/")) {
+ required_devices_partition_names_.emplace(basename(device.c_str()));
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
+ if (!required_devices_partition_names_.empty()) {
+ uevent_listener_.Poll(uevent_callback, 10s);
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ }
+ }
+ } else {
+ InitMappedDevice(device);
+ }
}
- fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
+ fs_mgr_overlayfs_mount_all(&fstab_);
return true;
}
+void FirstStageMount::UseGsiIfPresent() {
+ std::string metadata_file, error;
+
+ if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+ LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+ return;
+ }
+
+ auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+ if (!metadata) {
+ LOG(ERROR) << "GSI partition layout could not be read";
+ return;
+ }
+
+ if (!InitDmLinearBackingDevices(*metadata.get())) {
+ return;
+ }
+
+ // Find the name of the super partition for the GSI. It will either be
+ // "userdata", or a block device such as an sdcard. There are no by-name
+ // partitions other than userdata that we support installing GSIs to.
+ auto super = GetMetadataSuperBlockDevice(*metadata.get());
+ std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+ std::string super_path;
+ if (super_name == "userdata") {
+ super_path = "/dev/block/by-name/" + super_name;
+ } else {
+ super_path = "/dev/block/" + super_name;
+ }
+
+ if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+ LOG(ERROR) << "GSI partition layout could not be instantiated";
+ return;
+ }
+
+ if (!android::gsi::MarkSystemAsGsi()) {
+ PLOG(ERROR) << "GSI indicator file could not be written";
+ return;
+ }
+
+ // Replace the existing system fstab entry.
+ auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+ return entry.mount_point == "/system";
+ });
+ if (system_partition != fstab_.end()) {
+ fstab_.erase(system_partition);
+ }
+ fstab_.emplace_back(BuildGsiSystemFstabEntry());
+ gsi_not_on_userdata_ = (super_name != "userdata");
+}
+
bool FirstStageMountVBootV1::GetDmVerityDevices() {
- std::string verity_loc_device;
need_dm_verity_ = false;
- for (auto fstab_rec : mount_fstab_recs_) {
+ for (const auto& fstab_entry : fstab_) {
// Don't allow verifyatboot in the first stage.
- if (fs_mgr_is_verifyatboot(fstab_rec)) {
+ if (fstab_entry.fs_mgr_flags.verify_at_boot) {
LOG(ERROR) << "Partitions can't be verified at boot";
return false;
}
// Checks for verified partitions.
- if (fs_mgr_is_verified(fstab_rec)) {
+ if (fstab_entry.fs_mgr_flags.verify) {
need_dm_verity_ = true;
}
- // Checks if verity metadata is on a separate partition. Note that it is
- // not partition specific, so there must be only one additional partition
- // that carries verity state.
- if (fstab_rec->verity_loc) {
- if (verity_loc_device.empty()) {
- verity_loc_device = fstab_rec->verity_loc;
- } else if (verity_loc_device != fstab_rec->verity_loc) {
- LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
- << fstab_rec->verity_loc;
- return false;
- }
- }
}
- // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Includes the partition names of fstab records.
// Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
- for (auto fstab_rec : mount_fstab_recs_) {
- required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
- }
-
- if (!verity_loc_device.empty()) {
- required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
+ for (const auto& fstab_entry : fstab_) {
+ if (!fstab_entry.fs_mgr_flags.logical) {
+ required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
+ }
}
return true;
}
-bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
- if (fs_mgr_is_verified(fstab_rec)) {
- int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
+ if (fstab_entry->fs_mgr_flags.verify) {
+ int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */);
switch (ret) {
case FS_MGR_SETUP_VERITY_SKIPPED:
case FS_MGR_SETUP_VERITY_DISABLED:
- LOG(INFO) << "Verity disabled/skipped for '" << fstab_rec->mount_point << "'";
+ LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'";
return true;
case FS_MGR_SETUP_VERITY_SUCCESS:
// The exact block device name (fstab_rec->blk_device) is changed to
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
// first stage.
- return InitMappedDevice(fstab_rec->blk_device);
+ return InitMappedDevice(fstab_entry->blk_device);
default:
return false;
}
@@ -463,22 +681,27 @@
return true; // Returns true to mount the partition.
}
-// FirstStageMountVBootV2 constructor.
-// Gets the vbmeta partitions from device tree.
-// /{
-// firmware {
-// android {
-// vbmeta {
-// compatible = "android,vbmeta";
-// parts = "vbmeta,boot,system,vendor"
-// };
-// };
-// };
-// }
-FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
- if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
- PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
- return;
+// First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
+// for any further vbmeta partitions.
+FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
+ : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) {
+ std::string device_tree_vbmeta_parts;
+ read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts);
+
+ for (auto&& partition : Split(device_tree_vbmeta_parts, ",")) {
+ if (!partition.empty()) {
+ vbmeta_partitions_.emplace_back(std::move(partition));
+ }
+ }
+
+ for (const auto& entry : fstab_) {
+ if (!entry.vbmeta_partition.empty()) {
+ vbmeta_partitions_.emplace_back(entry.vbmeta_partition);
+ }
+ }
+
+ if (vbmeta_partitions_.empty()) {
+ LOG(ERROR) << "Failed to read vbmeta partitions.";
}
}
@@ -488,30 +711,27 @@
std::set<std::string> logical_partitions;
// fstab_rec->blk_device has A/B suffix.
- for (auto fstab_rec : mount_fstab_recs_) {
- if (fs_mgr_is_avb(fstab_rec)) {
+ for (const auto& fstab_entry : fstab_) {
+ if (fstab_entry.fs_mgr_flags.avb) {
need_dm_verity_ = true;
}
- if (fs_mgr_is_logical(fstab_rec)) {
+ if (fstab_entry.fs_mgr_flags.logical) {
// Don't try to find logical partitions via uevent regeneration.
- logical_partitions.emplace(basename(fstab_rec->blk_device));
+ logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
} else {
- required_devices_partition_names_.emplace(basename(fstab_rec->blk_device));
+ required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
}
}
- // libavb verifies AVB metadata on all verified partitions at once.
- // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
- // for libavb to verify metadata, even if there is only /vendor in the
- // above mount_fstab_recs_.
+ // Any partitions needed for verifying the partitions used in first stage mount, e.g. vbmeta
+ // must be provided as vbmeta_partitions.
if (need_dm_verity_) {
- if (device_tree_vbmeta_parts_.empty()) {
- LOG(ERROR) << "Missing vbmeta parts in device tree";
+ if (vbmeta_partitions_.empty()) {
+ LOG(ERROR) << "Missing vbmeta partitions";
return false;
}
- std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
std::string ab_suffix = fs_mgr_get_slot_suffix();
- for (const auto& partition : partitions) {
+ for (const auto& partition : vbmeta_partitions_) {
std::string partition_name = partition + ab_suffix;
if (logical_partitions.count(partition_name)) {
continue;
@@ -526,66 +746,65 @@
return true;
}
-ListenerAction FirstStageMountVBootV2::UeventCallback(const Uevent& uevent) {
- // Check if this uevent corresponds to one of the required partitions and store its symlinks if
- // so, in order to create FsManagerAvbHandle later.
- // Note that the parent callback removes partitions from the list of required partitions
- // as it finds them, so this must happen first.
- if (!uevent.partition_name.empty() &&
- required_devices_partition_names_.find(uevent.partition_name) !=
- required_devices_partition_names_.end()) {
- // GetBlockDeviceSymlinks() will return three symlinks at most, depending on
- // the content of uevent. by-name symlink will be at [0] if uevent->partition_name
- // is not empty. e.g.,
- // - /dev/block/platform/soc.0/f9824900.sdhci/by-name/modem
- // - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
- if (!links.empty()) {
- auto [it, inserted] = by_name_symlink_map_.emplace(uevent.partition_name, links[0]);
- if (!inserted) {
- LOG(ERROR) << "Partition '" << uevent.partition_name
- << "' already existed in the by-name symlink map with a value of '"
- << it->second << "', new value '" << links[0] << "' will be ignored.";
+bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
+ AvbHashtreeResult hashtree_result;
+
+ // It's possible for a fstab_entry to have both avb_keys and avb flag.
+ // In this case, try avb_keys first, then fallback to avb flag.
+ if (!fstab_entry->avb_keys.empty()) {
+ if (!InitAvbHandle()) return false;
+ // Checks if hashtree should be disabled from the top-level /vbmeta.
+ if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
+ avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
+ LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
+ << fstab_entry->mount_point;
+ return true; // Returns true to mount the partition directly.
+ } else {
+ auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+ if (!avb_standalone_handle) {
+ LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+ // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
+ if (!fstab_entry->fs_mgr_flags.avb) return false;
+ LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
+ hashtree_result =
+ avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+ } else {
+ // Sets up hashtree via the standalone handle.
+ if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+ return false;
+ }
+ hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
+ fstab_entry, false /* wait_for_verity_dev */);
}
}
- }
-
- return FirstStageMount::UeventCallback(uevent);
-}
-
-bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
- if (fs_mgr_is_avb(fstab_rec)) {
+ } else if (fstab_entry->fs_mgr_flags.avb) {
if (!InitAvbHandle()) return false;
- SetUpAvbHashtreeResult hashtree_result =
- avb_handle_->SetUpAvbHashtree(fstab_rec, false /* wait_for_verity_dev */);
- switch (hashtree_result) {
- case SetUpAvbHashtreeResult::kDisabled:
- return true; // Returns true to mount the partition.
- case SetUpAvbHashtreeResult::kSuccess:
- // The exact block device name (fstab_rec->blk_device) is changed to
- // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
- // first stage.
- return InitMappedDevice(fstab_rec->blk_device);
- default:
- return false;
- }
+ hashtree_result =
+ avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+ } else {
+ return true; // No need AVB, returns true to mount the partition directly.
}
- return true; // Returns true to mount the partition.
+
+ switch (hashtree_result) {
+ case AvbHashtreeResult::kDisabled:
+ return true; // Returns true to mount the partition.
+ case AvbHashtreeResult::kSuccess:
+ // The exact block device name (fstab_rec->blk_device) is changed to
+ // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+ // first stage.
+ return InitMappedDevice(fstab_entry->blk_device);
+ default:
+ return false;
+ }
}
bool FirstStageMountVBootV2::InitAvbHandle() {
if (avb_handle_) return true; // Returns true if the handle is already initialized.
- if (by_name_symlink_map_.empty()) {
- LOG(ERROR) << "by_name_symlink_map_ is empty";
- return false;
- }
-
- avb_handle_ = FsManagerAvbHandle::Open(std::move(by_name_symlink_map_));
- by_name_symlink_map_.clear(); // Removes all elements after the above std::move().
+ avb_handle_ = AvbHandle::Open();
if (!avb_handle_) {
- PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+ PLOG(ERROR) << "Failed to open AvbHandle";
return false;
}
// Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
@@ -617,26 +836,27 @@
return;
}
- if (!IsDtVbmetaCompatible()) {
+ auto fstab = ReadFirstStageFstab();
+
+ if (!IsDtVbmetaCompatible(fstab)) {
LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
return;
}
- // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+ // Initializes required devices for the subsequent AvbHandle::Open()
// to verify AVB metadata on all partitions in the verified chain.
// We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
// Open() function returns a valid handle.
// We don't need to mount partitions here in recovery mode.
- FirstStageMountVBootV2 avb_first_mount;
+ FirstStageMountVBootV2 avb_first_mount(std::move(fstab));
if (!avb_first_mount.InitDevices()) {
LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
return;
}
- FsManagerAvbUniquePtr avb_handle =
- FsManagerAvbHandle::Open(std::move(avb_first_mount.by_name_symlink_map_));
+ AvbUniquePtr avb_handle = AvbHandle::Open();
if (!avb_handle) {
- PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+ PLOG(ERROR) << "Failed to open AvbHandle for INIT_AVB_VERSION";
return;
}
setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index c4ff6df..2c56698 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -17,6 +17,6 @@
adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
done
(cd $TMPDIR && tar -czf $TARBALL $FILES)
-bootchart ${TMPDIR}/${TARBALL}
-gnome-open ${TARBALL%.tgz}.png
+pybootchartgui ${TMPDIR}/${TARBALL}
+xdg-open ${TARBALL%.tgz}.png
echo "Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done"
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
new file mode 100755
index 0000000..6afcb17
--- /dev/null
+++ b/init/host_builtin_map.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+"""Generates the builtins map to be used by host_init_verifier.
+
+It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
+equivalent check_xxx() if found in check_builtins.cpp.
+
+"""
+
+import re
+import argparse
+
+parser = argparse.ArgumentParser('host_builtin_map.py')
+parser.add_argument('--builtins', required=True, help='Path to builtins.cpp')
+parser.add_argument('--check_builtins', required=True, help='Path to check_builtins.cpp')
+args = parser.parse_args()
+
+CHECK_REGEX = re.compile(r'.+check_(\S+)\(.+')
+check_functions = []
+with open(args.check_builtins) as check_file:
+ for line in check_file:
+ match = CHECK_REGEX.match(line)
+ if match:
+ check_functions.append(match.group(1))
+
+function_map = []
+with open(args.builtins) as builtins_file:
+ in_function_map = False
+ for line in builtins_file:
+ if '// Builtin-function-map start' in line:
+ in_function_map = True
+ elif '// Builtin-function-map end' in line:
+ in_function_map = False
+ elif in_function_map:
+ function_map.append(line)
+
+DO_REGEX = re.compile(r'.+do_([^\}]+).+')
+FUNCTION_REGEX = re.compile(r'(do_[^\}]+)')
+for line in function_map:
+ match = DO_REGEX.match(line)
+ if match:
+ if match.group(1) in check_functions:
+ print line.replace('do_', 'check_'),
+ else:
+ print FUNCTION_REGEX.sub('check_stub', line),
+ else:
+ print line,
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index 93e363f..aa80199 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,16 +23,16 @@
namespace android {
namespace init {
-Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
- int) {
+Result<void> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+ int) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
- return Success();
+ return {};
}
-Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<void> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
return Error() << "Unexpected line found after import statement";
}
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index 52b8891..d6f7286 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -27,8 +27,8 @@
class HostImportParser : public SectionParser {
public:
HostImportParser() {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
- Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+ Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
};
} // namespace init
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
deleted file mode 100644
index b85e54a..0000000
--- a/init/host_init_stubs.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "host_init_stubs.h"
-
-#include <android-base/properties.h>
-
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list) {
- return 0;
-}
-
-namespace android {
-namespace init {
-
-// init.h
-std::string default_console = "/dev/console";
-
-// property_service.h
-bool CanReadProperty(const std::string& source_context, const std::string& name) {
- return true;
-}
-uint32_t SetProperty(const std::string& key, const std::string& value) {
- android::base::SetProperty(key, value);
- return 0;
-}
-uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
-uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
- std::string*) {
- return 0;
-}
-
-// selinux.h
-int SelinuxGetVendorAndroidVersion() {
- return 10000;
-}
-void SelabelInitialize() {}
-
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
- return false;
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 63ceead..f9a08a5 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _INIT_HOST_INIT_STUBS_H
-#define _INIT_HOST_INIT_STUBS_H
+#pragma once
#include <stddef.h>
#include <sys/socket.h>
@@ -23,33 +22,48 @@
#include <string>
+#include <android-base/properties.h>
+
// android/api-level.h
#define __ANDROID_API_P__ 28
+#define __ANDROID_API_R__ 30
// sys/system_properties.h
#define PROP_VALUE_MAX 92
-// unistd.h
-int setgroups(size_t __size, const gid_t* __list);
-
namespace android {
namespace init {
-// init.h
-extern std::string default_console;
-
// property_service.h
-bool CanReadProperty(const std::string& source_context, const std::string& name);
-extern uint32_t (*property_set)(const std::string& name, const std::string& value);
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error);
+inline bool CanReadProperty(const std::string&, const std::string&) {
+ return true;
+}
+inline uint32_t SetProperty(const std::string& key, const std::string& value) {
+ android::base::SetProperty(key, value);
+ return 0;
+}
+inline uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
+inline uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
+ const ucred&, std::string*) {
+ return 0;
+}
+
+// reboot_utils.h
+inline void SetFatalRebootTarget() {}
+inline void __attribute__((noreturn)) InitFatalReboot() {
+ abort();
+}
+
+// selabel.h
+inline void SelabelInitialize() {}
+inline bool SelabelLookupFileContext(const std::string&, int, std::string*) {
+ return false;
+}
// selinux.h
-int SelinuxGetVendorAndroidVersion();
-void SelabelInitialize();
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+inline int SelinuxGetVendorAndroidVersion() {
+ return 10000;
+}
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 8407729..a5a5b1b 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -15,11 +15,14 @@
//
#include <errno.h>
+#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
+#include <fstream>
#include <iostream>
+#include <iterator>
#include <string>
#include <vector>
@@ -27,15 +30,19 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <json/json.h>
#include "action.h"
#include "action_manager.h"
#include "action_parser.h"
+#include "check_builtins.h"
#include "host_import_parser.h"
#include "host_init_stubs.h"
#include "parser.h"
#include "result.h"
#include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
#define EXCLUDE_FS_CONFIG_STRUCTURES
#include "generated_android_ids.h"
@@ -46,9 +53,9 @@
using android::base::ReadFileToString;
using android::base::Split;
-static std::string passwd_file;
+static std::vector<std::string> passwd_files;
-static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
std::string passwd;
if (!ReadFileToString(passwd_file, &passwd)) {
return {};
@@ -70,6 +77,16 @@
return result;
}
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+ std::vector<std::pair<std::string, int>> result;
+ for (const auto& passwd_file : passwd_files) {
+ auto individual_result = GetVendorPasswd(passwd_file);
+ std::move(individual_result.begin(), individual_result.end(),
+ std::back_insert_iterator(result));
+ }
+ return result;
+}
+
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
// This isn't thread safe, but that's okay for our purposes.
static char static_name[32] = "";
@@ -115,44 +132,117 @@
return nullptr;
}
+static std::optional<android::init::InterfaceInheritanceHierarchyMap>
+ReadInterfaceInheritanceHierarchy(const std::string& interface_inheritance_hierarchy_file) {
+ if (interface_inheritance_hierarchy_file.empty()) {
+ LOG(WARNING) << "Missing an interface inheritance hierarchy file.";
+ return {};
+ }
+
+ Json::Value root;
+ Json::Reader reader;
+ std::ifstream stream(interface_inheritance_hierarchy_file);
+ if (!reader.parse(stream, root)) {
+ LOG(ERROR) << "Failed to read interface inheritance hierarchy file: "
+ << interface_inheritance_hierarchy_file << "\n"
+ << reader.getFormattedErrorMessages();
+ return {};
+ }
+
+ android::init::InterfaceInheritanceHierarchyMap result;
+ for (const Json::Value& entry : root) {
+ std::set<std::string> inherited_interfaces;
+ for (const Json::Value& intf : entry["inheritedInterfaces"]) {
+ inherited_interfaces.insert(intf.asString());
+ }
+ result[entry["interface"].asString()] = inherited_interfaces;
+ }
+
+ return result;
+}
+
namespace android {
namespace init {
-static Result<Success> do_stub(const BuiltinArguments& args) {
- return Success();
+static Result<void> check_stub(const BuiltinArguments& args) {
+ return {};
}
#include "generated_stub_builtin_function_map.h"
+void PrintUsage() {
+ std::cout << "usage: host_init_verifier [-p FILE] -k FILE <init rc file>\n"
+ "\n"
+ "Tests an init script for correctness\n"
+ "\n"
+ "-p FILE\tSearch this passwd file for users and groups\n"
+ "-k FILE\tUse this file as a space-separated list of known interfaces\n"
+ << std::endl;
+}
+
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- if (argc != 2 && argc != 3) {
- LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
+ std::string interface_inheritance_hierarchy_file;
+
+ while (true) {
+ static const struct option long_options[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
+
+ if (arg == -1) {
+ break;
+ }
+
+ switch (arg) {
+ case 'h':
+ PrintUsage();
+ return EXIT_FAILURE;
+ case 'p':
+ passwd_files.emplace_back(optarg);
+ break;
+ case 'i':
+ interface_inheritance_hierarchy_file = optarg;
+ break;
+ default:
+ std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ PrintUsage();
return EXIT_FAILURE;
}
- if (argc == 3) {
- passwd_file = argv[2];
- }
-
- const BuiltinFunctionMap function_map;
+ const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+ parser.AddSectionParser(
+ "service",
+ std::make_unique<ServiceParser>(
+ &sl, nullptr,
+ ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file)));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfigFileInsecure(argv[1])) {
- LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
+ if (!parser.ParseConfigFileInsecure(*argv)) {
+ LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
return EXIT_FAILURE;
}
- if (parser.parse_error_count() > 0) {
- LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
- << parser.parse_error_count() << " errors";
+ size_t failures = parser.parse_error_count() + am.CheckAllCommands();
+ if (failures > 0) {
+ LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
+ << " errors";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index fb3185e..1a43508 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,25 +23,24 @@
namespace android {
namespace init {
-Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
- std::string conf_file;
- bool ret = expand_props(args[1], &conf_file);
- if (!ret) {
- return Error() << "error while expanding import";
+ auto conf_file = ExpandProps(args[1]);
+ if (!conf_file) {
+ return Error() << "Could not expand import: " << conf_file.error();
}
- LOG(INFO) << "Added '" << conf_file << "' to import list";
+ LOG(INFO) << "Added '" << *conf_file << "' to import list";
if (filename_.empty()) filename_ = filename;
- imports_.emplace_back(std::move(conf_file), line);
- return Success();
+ imports_.emplace_back(std::move(*conf_file), line);
+ return {};
}
-Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<void> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
return Error() << "Unexpected line found after import statement";
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 7bc72e6..5bf9c6c 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,9 +28,9 @@
class ImportParser : public SectionParser {
public:
ImportParser(Parser* parser) : parser_(parser) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index b12ba8c..18fb0c3 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -28,43 +28,49 @@
#include <sys/types.h>
#include <unistd.h>
+#include <functional>
#include <map>
#include <memory>
#include <optional>
+#include <vector>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/android_reboot.h>
+#include <fs_avb/fs_avb.h>
+#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
#include <libavb/libavb.h>
+#include <libgsi/libgsi.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
#include <selinux/android.h>
-#ifndef RECOVERY
-#include <binder/ProcessState.h>
-#endif
-
#include "action_parser.h"
+#include "boringssl_self_test.h"
+#include "builtins.h"
#include "epoll.h"
+#include "first_stage_init.h"
#include "first_stage_mount.h"
#include "import_parser.h"
#include "keychords.h"
+#include "mount_handler.h"
+#include "mount_namespace.h"
#include "property_service.h"
#include "reboot.h"
#include "reboot_utils.h"
#include "security.h"
+#include "selabel.h"
#include "selinux.h"
+#include "service.h"
+#include "service_parser.h"
#include "sigchld_handler.h"
-#include "ueventd.h"
#include "util.h"
-#if __has_feature(address_sanitizer)
-#include <sanitizer/asan_interface.h>
-#endif
-
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -74,35 +80,15 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::fs_mgr::AvbHandle;
namespace android {
namespace init {
-#if __has_feature(address_sanitizer)
-// Load asan.options if it exists since these are not yet in the environment.
-// Always ensure detect_container_overflow=0 as there are false positives with this check.
-// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
-extern "C" const char* __asan_default_options() {
- return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
-__sanitizer_report_error_summary(const char* summary) {
- LOG(ERROR) << "Main stage (error summary): " << summary;
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
-AsanReportCallback(const char* str) {
- LOG(ERROR) << "Main stage: " << str;
-}
-#endif
-
static int property_triggers_enabled = 0;
static char qemu[32];
-std::string default_console = "/dev/console";
-
static int signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -111,8 +97,7 @@
static bool shutting_down;
static std::string shutdown_command;
static bool do_shutdown = false;
-
-std::vector<std::string> late_import_paths;
+static bool load_debug_prop = false;
static std::vector<Subcontext>* subcontexts;
@@ -124,13 +109,23 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
+// parser that only accepts new services
+Parser CreateServiceOnlyParser(ServiceList& service_list) {
+ Parser parser;
+
+ parser.AddSectionParser(
+ "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
+ return parser;
+}
+
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
@@ -140,12 +135,12 @@
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
+ // late_import is available only in Q and earlier release. As we don't
+ // have system_ext in those versions, skip late_import for system_ext.
+ parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
- if (!parser.ParseConfig("/product_services/etc/init")) {
- late_import_paths.emplace_back("/product_services/etc/init");
- }
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
@@ -202,9 +197,18 @@
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+ // We always record how long init waited for ueventd to tell us cold boot finished.
+ // If we aren't waiting on this property, it means that ueventd finished before we even started
+ // to wait.
+ if (name == kColdBootDoneProp) {
+ auto time_waited = waiting_for_prop ? waiting_for_prop->duration().count() : 0;
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(time_waited));
+ }
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
- LOG(INFO) << "Wait for property took " << *waiting_for_prop;
+ LOG(INFO) << "Wait for property '" << wait_prop_name << "=" << wait_prop_value
+ << "' took " << *waiting_for_prop;
ResetWaitForProp();
}
}
@@ -240,18 +244,18 @@
return next_process_action_time;
}
-static Result<Success> DoControlStart(Service* service) {
+static Result<void> DoControlStart(Service* service) {
return service->Start();
}
-static Result<Success> DoControlStop(Service* service) {
+static Result<void> DoControlStop(Service* service) {
service->Stop();
- return Success();
+ return {};
}
-static Result<Success> DoControlRestart(Service* service) {
+static Result<void> DoControlRestart(Service* service) {
service->Restart();
- return Success();
+ return {};
}
enum class ControlTarget {
@@ -261,16 +265,16 @@
struct ControlMessageFunction {
ControlTarget target;
- std::function<Result<Success>(Service*)> action;
+ std::function<Result<void>(Service*)> action;
};
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
// clang-format off
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
{"sigstop_on", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(true); return Success(); }}},
+ [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}},
{"sigstop_off", {ControlTarget::SERVICE,
- [](auto* service) { service->set_sigstop(false); return Success(); }}},
+ [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
{"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
@@ -283,13 +287,13 @@
return control_message_functions;
}
-void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << msg << "'";
- return;
+ return false;
}
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
@@ -318,46 +322,38 @@
default:
LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
<< static_cast<std::underlying_type<ControlTarget>::type>(function.target);
- return;
+ return false;
}
if (svc == nullptr) {
LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
- return;
+ return false;
}
if (auto result = function.action(svc); !result) {
LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
+ return false;
}
+ return true;
}
-static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
- Timer t;
-
- LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
-
- // Historically we had a 1s timeout here because we weren't otherwise
- // tracking boot time, and many OEMs made their sepolicy regular
- // expressions too expensive (http://b/19899875).
-
- // Now we're tracking boot time, just log the time taken to a system
- // property. We still panic if it takes more than a minute though,
- // because any build that slow isn't likely to boot at all, and we'd
- // rather any test lab devices fail back to the bootloader.
- if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
- LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;
+static Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {
+ if (!start_waiting_for_property(kColdBootDoneProp, "true")) {
+ LOG(FATAL) << "Could not wait for '" << kColdBootDoneProp << "'";
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
- return Success();
+ return {};
}
-static Result<Success> console_init_action(const BuiltinArguments& args) {
- std::string console = GetProperty("ro.boot.console", "");
- if (!console.empty()) {
- default_console = "/dev/" + console;
+static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
+ // Have to create <CGROUPS_RC_DIR> using make_dir function
+ // for appropriate sepolicy to be set for it
+ make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
+ if (!CgroupSetup()) {
+ return ErrnoError() << "Failed to setup cgroups";
}
- return Success();
+
+ return {};
}
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -441,32 +437,16 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
+static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
- return Success();
+ return {};
}
-static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
+static Result<void> queue_property_triggers_action(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyActions();
- return Success();
-}
-
-static Result<Success> InitBinder(const BuiltinArguments& args) {
- // init's use of binder is very limited. init cannot:
- // - have any binder threads
- // - receive incoming binder calls
- // - pass local binder services to remote processes
- // - use death recipients
- // The main supported usecases are:
- // - notifying other daemons (oneway calls only)
- // - retrieving data that is necessary to boot
- // Also, binder can't be used by recovery.
-#ifndef RECOVERY
- android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
-#endif
- return Success();
+ return {};
}
// Set the UDC controller for the ConfigFS USB Gadgets.
@@ -593,17 +573,6 @@
}
}
-static void InitAborter(const char* abort_message) {
- // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
- // simply abort instead of trying to reboot the system.
- if (getpid() != 1) {
- android::base::DefaultAborter(abort_message);
- return;
- }
-
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
-}
-
static void GlobalSeccomp() {
import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
bool in_qemu) {
@@ -613,61 +582,53 @@
});
}
-static void SetupSelinux(char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain and want to transition to the init domain. File systems that
- // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
- // but other file systems do. In particular, this is needed for ramdisks such as the
- // recovery image for A/B devices.
- if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+static void UmountDebugRamdisk() {
+ if (umount("/debug_ramdisk") != 0) {
+ LOG(ERROR) << "Failed to umount /debug_ramdisk";
}
-
- setenv("SELINUX_INITIALIZED", "true", 1);
-
- const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
- execv(path, const_cast<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never return from this function.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
-int main(int argc, char** argv) {
-#if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
-#endif
-
- if (!strcmp(basename(argv[0]), "ueventd")) {
- return ueventd_main(argc, argv);
+static void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {
+ int64_t first_stage_start_time_ns = -1;
+ if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);
+ first_stage_start_time_str) {
+ property_set("ro.boottime.init", first_stage_start_time_str);
+ android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);
}
+ unsetenv(kEnvFirstStageStartedAt);
- if (argc > 1 && !strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap function_map;
- return SubcontextMain(argc, argv, &function_map);
+ int64_t selinux_start_time_ns = -1;
+ if (auto selinux_start_time_str = getenv(kEnvSelinuxStartedAt); selinux_start_time_str) {
+ android::base::ParseInt(selinux_start_time_str, &selinux_start_time_ns);
}
+ unsetenv(kEnvSelinuxStartedAt);
+ if (selinux_start_time_ns == -1) return;
+ if (first_stage_start_time_ns == -1) return;
+
+ property_set("ro.boottime.init.first_stage",
+ std::to_string(selinux_start_time_ns - first_stage_start_time_ns));
+ property_set("ro.boottime.init.selinux",
+ std::to_string(second_stage_start_time.time_since_epoch().count() -
+ selinux_start_time_ns));
+}
+
+int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
- if (getenv("SELINUX_INITIALIZED") == nullptr) {
- SetupSelinux(argv);
- }
+ boot_clock::time_point start_time = boot_clock::now();
- // We need to set up stdin/stdout/stderr again now that we're running in init's context.
- InitKernelLogging(argv, InitAborter);
+ SetStdioToDevNull(argv);
+ InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
+ // Set init and its forked children's oom_adj.
+ if (auto result = WriteFile("/proc/1/oom_score_adj", "-1000"); !result) {
+ LOG(ERROR) << "Unable to write -1000 to /proc/1/oom_score_adj: " << result.error();
+ }
+
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
GlobalSeccomp();
@@ -690,19 +651,22 @@
// used by init as well as the current required properties.
export_kernel_boot_props();
- // Make the time that init started available for bootstat to log.
- property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
- property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
+ // Make the time that init stages started available for bootstat to log.
+ RecordStageBoottimes(start_time);
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
+ // See if need to load debug props to allow adb root, when the device is unlocked.
+ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+ if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
+ load_debug_prop = "true"s == force_debuggable_env;
+ }
+
// Clean up our environment.
- unsetenv("SELINUX_INITIALIZED");
- unsetenv("INIT_STARTED_AT");
- unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
+ unsetenv("INIT_FORCE_DEBUGGABLE");
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
@@ -716,14 +680,21 @@
InstallSignalFdHandler(&epoll);
- property_load_boot_defaults();
+ property_load_boot_defaults(load_debug_prop);
+ UmountDebugRamdisk();
+ fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
StartPropertyService(&epoll);
+ MountHandler mount_handler(&epoll);
set_usb_controller();
- const BuiltinFunctionMap function_map;
+ const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
+ if (!SetupMountNamespaces()) {
+ PLOG(FATAL) << "SetupMountNamespaces failed";
+ }
+
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
@@ -735,6 +706,15 @@
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
+ // Make the GSI status available before scripts start running.
+ if (android::gsi::IsGsiRunning()) {
+ property_set("ro.gsid.image_running", "1");
+ } else {
+ property_set("ro.gsid.image_running", "0");
+ }
+
+ am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
+
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -745,26 +725,25 @@
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
Keychords keychords;
am.QueueBuiltinAction(
- [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
- for (const auto& svc : ServiceList::GetInstance()) {
- keychords.Register(svc->keycodes());
- }
- keychords.Start(&epoll, HandleKeychord);
- return Success();
- },
- "KeychordInit");
- am.QueueBuiltinAction(console_init_action, "console_init");
+ [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
+ for (const auto& svc : ServiceList::GetInstance()) {
+ keychords.Register(svc->keycodes());
+ }
+ keychords.Start(&epoll, HandleKeychord);
+ return {};
+ },
+ "KeychordInit");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
+ // Starting the BoringSSL self test, for NIAP certification compliance.
+ am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
+
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
- // Initialize binder before bringing up other system services
- am.QueueBuiltinAction(InitBinder, "InitBinder");
-
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
diff --git a/init/init.h b/init/init.h
index f244ad7..cfc28f1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -14,32 +14,24 @@
* limitations under the License.
*/
-#ifndef _INIT_INIT_H
-#define _INIT_INIT_H
+#pragma once
#include <sys/types.h>
-#include <functional>
#include <string>
-#include <vector>
#include "action.h"
#include "action_manager.h"
#include "parser.h"
-#include "service.h"
+#include "service_list.h"
namespace android {
namespace init {
-// Note: These globals are *only* valid in init, so they should not be used in ueventd
-// or any files that may be included in ueventd, such as devices.cpp and util.cpp.
-// TODO: Have an Init class and remove all globals.
-extern std::string default_console;
-extern std::vector<std::string> late_import_paths;
-
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
+Parser CreateServiceOnlyParser(ServiceList& service_list);
-void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
+bool HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
void property_changed(const std::string& name, const std::string& value);
@@ -49,9 +41,7 @@
void ResetWaitForProp();
-int main(int argc, char** argv);
+int SecondStageMain(int argc, char** argv);
} // namespace init
} // namespace android
-
-#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
deleted file mode 100644
index d81ca5c..0000000
--- a/init/init_first_stage.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <paths.h>
-#include <stdlib.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <string>
-#include <vector>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <cutils/android_reboot.h>
-#include <private/android_filesystem_config.h>
-
-#include "first_stage_mount.h"
-#include "reboot_utils.h"
-#include "util.h"
-
-using android::base::boot_clock;
-
-namespace android {
-namespace init {
-
-int main(int argc, char** argv) {
- if (REBOOT_BOOTLOADER_ON_PANIC) {
- InstallRebootSignalHandlers();
- }
-
- boot_clock::time_point start_time = boot_clock::now();
-
- std::vector<std::pair<std::string, int>> errors;
-#define CHECKCALL(x) \
- if (x != 0) errors.emplace_back(#x " failed", errno);
-
- // Clear the umask.
- umask(0);
-
- CHECKCALL(clearenv());
- CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
- // Get the basic filesystem setup we need put together in the initramdisk
- // on / and then we'll let the rc file figure out the rest.
- CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
- CHECKCALL(mkdir("/dev/pts", 0755));
- CHECKCALL(mkdir("/dev/socket", 0755));
- CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
-#define MAKE_STR(x) __STRING(x)
- CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
-#undef MAKE_STR
- // Don't expose the raw commandline to unprivileged processes.
- CHECKCALL(chmod("/proc/cmdline", 0440));
- gid_t groups[] = {AID_READPROC};
- CHECKCALL(setgroups(arraysize(groups), groups));
- CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
- CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
-
- CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));
-
- if constexpr (WORLD_WRITABLE_KMSG) {
- CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
- }
-
- CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
- CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
-
- // This is needed for log wrapper, which gets called before ueventd runs.
- CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
- CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
-
- // These below mounts are done in first stage init so that first stage mount can mount
- // subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
- // should be done in rc files.
- // Mount staging areas for devices managed by vold
- // See storage config details at http://source.android.com/devices/storage/
- CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=1000"));
- // /mnt/vendor is used to mount vendor-specific partitions that can not be
- // part of the vendor partition, e.g. because they are mounted read-write.
- CHECKCALL(mkdir("/mnt/vendor", 0755));
- // /mnt/product is used to mount product-specific partitions that can not be
- // part of the product partition, e.g. because they are mounted read-write.
- CHECKCALL(mkdir("/mnt/product", 0755));
-
-#undef CHECKCALL
-
- // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
- // talk to the outside world...
- // We need to set up stdin/stdout/stderr for child processes forked from first
- // stage init as part of the mount process. This closes /dev/console if the
- // kernel had previously opened it.
- auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
- InitKernelLogging(argv, reboot_bootloader);
-
- if (!errors.empty()) {
- for (const auto& [error_string, error_errno] : errors) {
- LOG(ERROR) << error_string << " " << strerror(error_errno);
- }
- LOG(FATAL) << "Init encountered errors starting first stage, aborting";
- }
-
- LOG(INFO) << "init first stage started!";
-
- if (!DoFirstStageMount()) {
- LOG(FATAL) << "Failed to mount required partitions early ...";
- }
-
- SetInitAvbVersionInRecovery();
-
- static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
- uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
- setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
-
- const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
- execv(path, const_cast<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never fall through this conditional.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
-
- return 1;
-}
-
-} // namespace init
-} // namespace android
-
-int main(int argc, char** argv) {
- return android::init::main(argc, argv);
-}
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0f9635f..0411214 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,18 +17,19 @@
#include <functional>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "action.h"
#include "action_manager.h"
#include "action_parser.h"
+#include "builtin_arguments.h"
#include "builtins.h"
#include "import_parser.h"
#include "keyword_map.h"
#include "parser.h"
#include "service.h"
-#include "test_function_map.h"
+#include "service_list.h"
+#include "service_parser.h"
#include "util.h"
namespace android {
@@ -36,14 +37,15 @@
using ActionManagerCommand = std::function<void(ActionManager&)>;
-void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
ActionManager am;
Action::set_function_map(&test_function_map);
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
@@ -58,7 +60,7 @@
}
}
-void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
@@ -74,8 +76,13 @@
pass_test
)init";
- TestFunctionMap test_function_map;
- test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+ auto do_pass_test = [&expect_true](const BuiltinArguments&) {
+ expect_true = true;
+ return Result<void>{};
+ };
+ BuiltinFunctionMap test_function_map = {
+ {"pass_test", {0, 0, {false, do_pass_test}}},
+ };
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -101,10 +108,24 @@
)init";
int num_executed = 0;
- TestFunctionMap test_function_map;
- test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
- test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
- test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+ auto do_execute_first = [&num_executed](const BuiltinArguments&) {
+ EXPECT_EQ(0, num_executed++);
+ return Result<void>{};
+ };
+ auto do_execute_second = [&num_executed](const BuiltinArguments&) {
+ EXPECT_EQ(1, num_executed++);
+ return Result<void>{};
+ };
+ auto do_execute_third = [&num_executed](const BuiltinArguments&) {
+ EXPECT_EQ(2, num_executed++);
+ return Result<void>{};
+ };
+
+ BuiltinFunctionMap test_function_map = {
+ {"execute_first", {0, 0, {false, do_execute_first}}},
+ {"execute_second", {0, 0, {false, do_execute_second}}},
+ {"execute_third", {0, 0, {false, do_execute_third}}},
+ };
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -125,7 +146,7 @@
)init";
ServiceList service_list;
- TestInitText(init_script, TestFunctionMap(), {}, &service_list);
+ TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
auto service = service_list.begin()->get();
@@ -181,11 +202,12 @@
auto execute_command = [&num_executed](const BuiltinArguments& args) {
EXPECT_EQ(2U, args.size());
EXPECT_EQ(++num_executed, std::stoi(args[1]));
- return Success();
+ return Result<void>{};
};
- TestFunctionMap test_function_map;
- test_function_map.Add("execute", 1, 1, false, execute_command);
+ BuiltinFunctionMap test_function_map = {
+ {"execute", {1, 1, {false, execute_command}}},
+ };
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
diff --git a/init/keychords.cpp b/init/keychords.cpp
index f5ac44f..5f2682b 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -41,7 +41,7 @@
Keychords::~Keychords() noexcept {
if (inotify_fd_ >= 0) {
- epoll_->UnregisterHandler(inotify_fd_).IgnoreError();
+ epoll_->UnregisterHandler(inotify_fd_);
::close(inotify_fd_);
}
while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
@@ -212,7 +212,7 @@
auto it = registration_.find(device);
if (it == registration_.end()) return;
auto fd = (*it).second;
- epoll_->UnregisterHandler(fd).IgnoreError();
+ epoll_->UnregisterHandler(fd);
registration_.erase(it);
::close(fd);
}
@@ -285,7 +285,7 @@
void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
epoll_ = epoll;
- handler_ = handler;
+ handler_ = std::move(handler);
if (entries_.size()) GeteventOpenDevice();
}
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index e5a6fd3..33373d4 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -29,7 +29,6 @@
#include <vector>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
@@ -213,7 +212,7 @@
}
void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
- epoll_.Wait(wait).IgnoreError();
+ epoll_.Wait(wait);
}
void TestFrame::SetChord(int key, bool value) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
index c95fc73..d92678f 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -14,70 +14,74 @@
* limitations under the License.
*/
-#ifndef _INIT_KEYWORD_MAP_H_
-#define _INIT_KEYWORD_MAP_H_
+#pragma once
#include <map>
#include <string>
-
-#include <android-base/stringprintf.h>
+#include <vector>
#include "result.h"
namespace android {
namespace init {
-template <typename Function>
+// Every init builtin, init service option, and ueventd option has a minimum and maximum number of
+// arguments. These must be checked both at run time for safety and also at build time for
+// correctness in host_init_verifier. Instead of copying and pasting the boiler plate code that
+// does this check into each function, it is abstracted in KeywordMap<>. This class maps keywords
+// to functions and checks that the number of arguments provided falls in the correct range or
+// returns an error otherwise.
+
+// Value is the return value of Find(), which is typically either a single function or a struct with
+// additional information.
+template <typename Value>
class KeywordMap {
public:
- using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
- using Map = std::map<std::string, FunctionInfo>;
+ struct MapValue {
+ size_t min_args;
+ size_t max_args;
+ Value value;
+ };
- virtual ~KeywordMap() {
- }
+ KeywordMap() {}
+ KeywordMap(std::initializer_list<std::pair<const std::string, MapValue>> init) : map_(init) {}
- const Result<Function> FindFunction(const std::vector<std::string>& args) const {
- using android::base::StringPrintf;
-
+ Result<Value> Find(const std::vector<std::string>& args) const {
if (args.empty()) return Error() << "Keyword needed, but not provided";
auto& keyword = args[0];
auto num_args = args.size() - 1;
- auto function_info_it = map().find(keyword);
- if (function_info_it == map().end()) {
- return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
+ auto result_it = map_.find(keyword);
+ if (result_it == map_.end()) {
+ return Errorf("Invalid keyword '{}'", keyword);
}
- auto function_info = function_info_it->second;
+ auto result = result_it->second;
- auto min_args = std::get<0>(function_info);
- auto max_args = std::get<1>(function_info);
+ auto min_args = result.min_args;
+ auto max_args = result.max_args;
if (min_args == max_args && num_args != min_args) {
- return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
- (min_args > 1 || min_args == 0) ? "s" : "");
+ return Errorf("{} requires {} argument{}", keyword, min_args,
+ (min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
- return Error() << StringPrintf("%s requires at least %zu argument%s",
- keyword.c_str(), min_args, min_args > 1 ? "s" : "");
+ return Errorf("{} requires at least {} argument{}", keyword, min_args,
+ min_args > 1 ? "s" : "");
} else {
- return Error() << StringPrintf("%s requires between %zu and %zu arguments",
- keyword.c_str(), min_args, max_args);
+ return Errorf("{} requires between {} and {} arguments", keyword, min_args,
+ max_args);
}
}
- return std::get<Function>(function_info);
+ return result.value;
}
private:
- // Map of keyword ->
- // (minimum number of arguments, maximum number of arguments, function pointer)
- virtual const Map& map() const = 0;
+ std::map<std::string, MapValue> map_;
};
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/main.cpp b/init/main.cpp
index 9ed451b..38bc74b 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -14,8 +14,65 @@
* limitations under the License.
*/
+#include "builtins.h"
+#include "first_stage_init.h"
#include "init.h"
+#include "selinux.h"
+#include "subcontext.h"
+#include "ueventd.h"
+
+#include <android-base/logging.h>
+
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+ return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+ LOG(ERROR) << "Init (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+ LOG(ERROR) << "Init: " << str;
+}
+#endif
+
+using namespace android::init;
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+#if __has_feature(address_sanitizer)
+ __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
+ if (!strcmp(basename(argv[0]), "ueventd")) {
+ return ueventd_main(argc, argv);
+ }
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "subcontext")) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
+
+ return SubcontextMain(argc, argv, &function_map);
+ }
+
+ if (!strcmp(argv[1], "selinux_setup")) {
+ return SetupSelinux(argv);
+ }
+
+ if (!strcmp(argv[1], "second_stage")) {
+ return SecondStageMain(argc, argv);
+ }
+ }
+
+ return FirstStageMain(argc, argv);
}
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index c61c210..07b05d8 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -16,147 +16,20 @@
#include "modalias_handler.h"
-#include <fnmatch.h>
-#include <sys/syscall.h>
-
-#include <algorithm>
-#include <functional>
#include <string>
#include <vector>
-#include <android-base/chrono_utils.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-
-#include "parser.h"
+#include <modprobe/modprobe.h>
namespace android {
namespace init {
-Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
- std::vector<std::string> deps;
-
- // Set first item as our modules path
- std::string::size_type pos = args[0].find(':');
- if (pos != std::string::npos) {
- deps.emplace_back(args[0].substr(0, pos));
- } else {
- return Error() << "dependency lines must start with name followed by ':'";
- }
-
- // Remaining items are dependencies of our module
- for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
- deps.push_back(*arg);
- }
-
- // Key is striped module name to match names in alias file
- std::size_t start = args[0].find_last_of('/');
- std::size_t end = args[0].find(".ko:");
- if ((end - start) <= 1) return Error() << "malformed dependency line";
- auto mod_name = args[0].substr(start + 1, (end - start) - 1);
- // module names can have '-', but their file names will have '_'
- std::replace(mod_name.begin(), mod_name.end(), '-', '_');
- this->module_deps_[mod_name] = deps;
-
- return Success();
-}
-
-Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
- auto it = args.begin();
- const std::string& type = *it++;
-
- if (type != "alias") {
- return Error() << "we only handle alias lines, got: " << type;
- }
-
- if (args.size() != 3) {
- return Error() << "alias lines must have 3 entries";
- }
-
- std::string& alias = *it++;
- std::string& module_name = *it++;
- this->module_aliases_.emplace_back(alias, module_name);
-
- return Success();
-}
-
-ModaliasHandler::ModaliasHandler() {
- using namespace std::placeholders;
-
- static const std::string base_paths[] = {
- "/vendor/lib/modules/",
- "/lib/modules/",
- "/odm/lib/modules/",
- };
-
- Parser alias_parser;
- auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
- alias_parser.AddSingleLineParser("alias", alias_callback);
- for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
-
- Parser dep_parser;
- auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
- dep_parser.AddSingleLineParser("", dep_callback);
- for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
-}
-
-Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
- base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
- if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
-
- int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
- if (ret != 0) {
- if (errno == EEXIST) {
- // Module already loaded
- return Success();
- }
- return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
- }
-
- LOG(INFO) << "Loaded kernel module " << path_name;
- return Success();
-}
-
-Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
- const std::string& args) {
- if (module_name.empty()) {
- return Error() << "Need valid module name";
- }
-
- auto it = module_deps_.find(module_name);
- if (it == module_deps_.end()) {
- return Error() << "Module '" << module_name << "' not in dependency file";
- }
- auto& dependencies = it->second;
-
- // load module dependencies in reverse order
- for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
- if (auto result = Insmod(*dep, ""); !result) return result;
- }
-
- // load target module itself with args
- return Insmod(dependencies[0], args);
-}
+ModaliasHandler::ModaliasHandler(const std::vector<std::string>& base_paths)
+ : modprobe_(base_paths) {}
void ModaliasHandler::HandleUevent(const Uevent& uevent) {
if (uevent.modalias.empty()) return;
-
- for (const auto& [alias, module] : module_aliases_) {
- if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue; // Keep looking
-
- LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
- << "'";
-
- if (auto result = InsmodWithDeps(module, ""); !result) {
- LOG(ERROR) << "Cannot load module: " << result.error();
- // try another one since there may be another match
- continue;
- }
-
- // loading was successful
- return;
- }
+ modprobe_.LoadWithAliases(uevent.modalias, true);
}
} // namespace init
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index 3247c86..ce89a05 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -17,10 +17,10 @@
#pragma once
#include <string>
-#include <unordered_map>
#include <vector>
-#include "result.h"
+#include <modprobe/modprobe.h>
+
#include "uevent.h"
#include "uevent_handler.h"
@@ -29,20 +29,13 @@
class ModaliasHandler : public UeventHandler {
public:
- ModaliasHandler();
+ ModaliasHandler(const std::vector<std::string>&);
virtual ~ModaliasHandler() = default;
void HandleUevent(const Uevent& uevent) override;
private:
- Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
- Result<Success> Insmod(const std::string& path_name, const std::string& args);
-
- Result<Success> ParseDepCallback(std::vector<std::string>&& args);
- Result<Success> ParseAliasCallback(std::vector<std::string>&& args);
-
- std::vector<std::pair<std::string, std::string>> module_aliases_;
- std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+ Modprobe modprobe_;
};
} // namespace init
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
new file mode 100644
index 0000000..791a019
--- /dev/null
+++ b/init/mount_handler.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mount_handler.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+
+#include "epoll.h"
+#include "property_service.h"
+
+namespace android {
+namespace init {
+
+namespace {
+
+MountHandlerEntry ParseMount(const std::string& line) {
+ auto fields = android::base::Split(line, " ");
+ while (fields.size() < 3) fields.emplace_back("");
+ if (fields[0] == "/dev/root") {
+ auto& dm = dm::DeviceMapper::Instance();
+ std::string path;
+ if (dm.GetDmDevicePathByName("system", &path) || dm.GetDmDevicePathByName("vroot", &path)) {
+ fields[0] = path;
+ } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {
+ auto entry = GetEntryForMountPoint(&fstab, "/");
+ if (entry || (entry = GetEntryForMountPoint(&fstab, "/system"))) {
+ fields[0] = entry->blk_device;
+ }
+ }
+ }
+ if (android::base::StartsWith(fields[0], "/dev/")) {
+ if (std::string link; android::base::Readlink(fields[0], &link)) {
+ fields[0] = link;
+ }
+ }
+ return MountHandlerEntry(fields[0], fields[1], fields[2]);
+}
+
+void SetMountProperty(const MountHandlerEntry& entry, bool add) {
+ static constexpr char devblock[] = "/dev/block/";
+ if (!android::base::StartsWith(entry.blk_device, devblock)) return;
+ std::string value;
+ if (add) {
+ value = entry.blk_device.substr(strlen(devblock));
+ if (android::base::StartsWith(value, "sd")) {
+ // All sd partitions inherit their queue characteristics
+ // from the whole device reference. Strip partition number.
+ auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+ if (it != value.end()) value.erase(it, value.end());
+ }
+ auto queue = "/sys/block/" + value + "/queue";
+ struct stat sb;
+ if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ // Clear the noise associated with loopback and APEX.
+ if (android::base::StartsWith(value, "loop")) value = "";
+ if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+ }
+ auto mount_prop = entry.mount_point;
+ if (mount_prop == "/") mount_prop = "/root";
+ std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
+ mount_prop = "dev.mnt.blk" + mount_prop;
+ // Set property even if its value does not change to trigger 'on property:'
+ // handling, except for clearing non-existent or already clear property.
+ // Goal is reduction of empty properties and associated triggers.
+ if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
+ property_set(mount_prop, value);
+}
+
+} // namespace
+
+MountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+ const std::string& fs_type)
+ : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}
+
+bool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {
+ if (blk_device < r.blk_device) return true;
+ if (blk_device > r.blk_device) return false;
+ if (mount_point < r.mount_point) return true;
+ if (mount_point > r.mount_point) return false;
+ return fs_type < r.fs_type;
+}
+
+MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {
+ if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";
+ auto result = epoll->RegisterHandler(
+ fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);
+ if (!result) LOG(FATAL) << result.error();
+}
+
+MountHandler::~MountHandler() {
+ if (fp_) epoll_->UnregisterHandler(fileno(fp_.get()));
+}
+
+void MountHandler::MountHandlerFunction() {
+ rewind(fp_.get());
+ std::vector<MountHandlerEntry> touched;
+ auto untouched = mounts_;
+ char* buf = nullptr;
+ size_t len = 0;
+ while (getline(&buf, &len, fp_.get()) != -1) {
+ auto entry = ParseMount(std::string(buf));
+ auto match = untouched.find(entry);
+ if (match == untouched.end()) {
+ touched.emplace_back(std::move(entry));
+ } else {
+ untouched.erase(match);
+ }
+ }
+ free(buf);
+ for (auto& entry : untouched) {
+ SetMountProperty(entry, false);
+ mounts_.erase(entry);
+ }
+ for (auto& entry : touched) {
+ SetMountProperty(entry, true);
+ mounts_.emplace(std::move(entry));
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/mount_handler.h b/init/mount_handler.h
new file mode 100644
index 0000000..e524a74
--- /dev/null
+++ b/init/mount_handler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "epoll.h"
+
+namespace android {
+namespace init {
+
+struct MountHandlerEntry {
+ MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,
+ const std::string& fs_type);
+
+ bool operator<(const MountHandlerEntry& r) const;
+
+ const std::string blk_device;
+ const std::string mount_point;
+ const std::string fs_type;
+};
+
+class MountHandler {
+ public:
+ explicit MountHandler(Epoll* epoll);
+ MountHandler(const MountHandler&) = delete;
+ MountHandler(MountHandler&&) = delete;
+ MountHandler& operator=(const MountHandler&) = delete;
+ MountHandler& operator=(MountHandler&&) = delete;
+ ~MountHandler();
+
+ private:
+ void MountHandlerFunction();
+
+ Epoll* epoll_;
+ std::unique_ptr<FILE, decltype(&fclose)> fp_;
+ std::set<MountHandlerEntry> mounts_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
new file mode 100644
index 0000000..12144c1
--- /dev/null
+++ b/init/mount_namespace.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mount_namespace.h"
+
+#include <sys/mount.h>
+
+#include <string>
+#include <vector>
+
+#include <ApexProperties.sysprop.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+namespace android {
+namespace init {
+namespace {
+
+static bool MakeShared(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_SHARED;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to shared";
+ return false;
+ }
+ return true;
+}
+
+static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_PRIVATE;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to private";
+ return false;
+ }
+ return true;
+}
+
+static int OpenMountNamespace() {
+ int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ PLOG(ERROR) << "Cannot open fd for current mount namespace";
+ }
+ return fd;
+}
+
+static std::string GetMountNamespaceId() {
+ std::string ret;
+ if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) {
+ PLOG(ERROR) << "Failed to read namespace ID";
+ return "";
+ }
+ return ret;
+}
+
+static bool IsApexUpdatable() {
+ static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+ return updatable;
+}
+
+static bool ActivateFlattenedApexesIfPossible() {
+ if (IsRecoveryMode() || IsApexUpdatable()) {
+ return true;
+ }
+
+ constexpr const char kSystemApex[] = "/system/apex";
+ constexpr const char kApexTop[] = "/apex";
+ if (mount(kSystemApex, kApexTop, nullptr, MS_BIND, nullptr) != 0) {
+ PLOG(ERROR) << "Could not bind mount " << kSystemApex << " to " << kApexTop;
+ return false;
+ }
+
+ // Special casing for the runtime APEX
+ constexpr const char kRuntimeApexMountPath[] = "/system/apex/com.android.runtime";
+ static const std::vector<std::string> kRuntimeApexDirNames = {"com.android.runtime.release",
+ "com.android.runtime.debug"};
+ bool success = false;
+ for (const auto& name : kRuntimeApexDirNames) {
+ std::string path = std::string(kSystemApex) + "/" + name;
+ if (access(path.c_str(), F_OK) == 0) {
+ if (mount(path.c_str(), kRuntimeApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
+ success = true;
+ break;
+ }
+ }
+ }
+ if (!success) {
+ PLOG(ERROR) << "Failed to bind mount the runtime APEX to " << kRuntimeApexMountPath;
+ }
+ return success;
+}
+
+static android::base::unique_fd bootstrap_ns_fd;
+static android::base::unique_fd default_ns_fd;
+
+static std::string bootstrap_ns_id;
+static std::string default_ns_id;
+
+} // namespace
+
+bool SetupMountNamespaces() {
+ // Set the propagation type of / as shared so that any mounting event (e.g.
+ // /data) is by default visible to all processes. When private mounting is
+ // needed for /foo/bar, then we will make /foo/bar as a mount point (by
+ // bind-mounting by to itself) and set the propagation type of the mount
+ // point to private.
+ if (!MakeShared("/", true /*recursive*/)) return false;
+
+ // /apex is a private mountpoint to give different sets of APEXes for
+ // the bootstrap and default mount namespaces. The processes running with
+ // the bootstrap namespace get APEXes from the read-only partition.
+ if (!(MakePrivate("/apex"))) return false;
+
+ bootstrap_ns_fd.reset(OpenMountNamespace());
+ bootstrap_ns_id = GetMountNamespaceId();
+
+ // When APEXes are updatable (e.g. not-flattened), we create separate mount
+ // namespaces for processes that are started before and after the APEX is
+ // activated by apexd. In the namespace for pre-apexd processes, small
+ // number of essential APEXes (e.g. com.android.runtime) are activated.
+ // In the namespace for post-apexd processes, all APEXes are activated.
+ bool success = true;
+ if (IsApexUpdatable() && !IsRecoveryMode()) {
+ // Creating a new namespace by cloning, saving, and switching back to
+ // the original namespace.
+ if (unshare(CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Cannot create mount namespace";
+ return false;
+ }
+ default_ns_fd.reset(OpenMountNamespace());
+ default_ns_id = GetMountNamespaceId();
+
+ if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
+ return false;
+ }
+ } else {
+ // Otherwise, default == bootstrap
+ default_ns_fd.reset(OpenMountNamespace());
+ default_ns_id = GetMountNamespaceId();
+ }
+
+ success &= ActivateFlattenedApexesIfPossible();
+
+ LOG(INFO) << "SetupMountNamespaces done";
+ return success;
+}
+
+bool SwitchToDefaultMountNamespace() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
+ }
+ if (default_ns_id != GetMountNamespaceId()) {
+ if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
+ return false;
+ }
+ }
+
+ LOG(INFO) << "Switched to default mount namespace";
+ return true;
+}
+
+bool SwitchToBootstrapMountNamespaceIfNeeded() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
+ }
+ if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
+ IsApexUpdatable()) {
+ if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/init/mount_namespace.h
similarity index 67%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to init/mount_namespace.h
index 410d379..c41a449 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/init/mount_namespace.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+namespace android {
+namespace init {
+
+bool SetupMountNamespaces();
+bool SwitchToDefaultMountNamespace();
+bool SwitchToBootstrapMountNamespaceIfNeeded();
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index bbfbdc6..6ab61cb 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -37,7 +37,7 @@
}
void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
- line_callbacks_.emplace_back(prefix, callback);
+ line_callbacks_.emplace_back(prefix, std::move(callback));
}
void Parser::ParseData(const std::string& filename, std::string* data) {
diff --git a/init/parser.h b/init/parser.h
index 2454b6a..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,7 +27,7 @@
// SectionParser is an interface that can parse a given 'section' in init.
//
// You can implement up to 4 functions below, with ParseSection being mandatory. The first two
-// functions return Result<Success> indicating if they have an error. It will be reported along
+// functions return Result<void> indicating if they have an error. It will be reported along
// with the filename and line number of where the error occurred.
//
// 1) ParseSection
@@ -51,10 +51,10 @@
class SectionParser {
public:
virtual ~SectionParser() {}
- virtual Result<Success> ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) = 0;
- virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
- virtual Result<Success> EndSection() { return Success(); };
+ virtual Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) = 0;
+ virtual Result<void> ParseLineSection(std::vector<std::string>&&, int) { return {}; };
+ virtual Result<void> EndSection() { return {}; };
virtual void EndFile(){};
};
@@ -67,11 +67,12 @@
// Similar to ParseSection() and ParseLineSection(), this function returns bool with false
// indicating a failure and has an std::string* err parameter into which an error string can
// be written.
- using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
+ using LineCallback = std::function<Result<void>(std::vector<std::string>&&)>;
Parser();
bool ParseConfig(const std::string& path);
+ bool ParseConfigFile(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
@@ -82,7 +83,6 @@
private:
void ParseData(const std::string& filename, std::string* data);
- bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 21adce9..baa9ad4 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -69,7 +69,7 @@
continue;
}
- unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+ unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
continue;
@@ -169,7 +169,7 @@
return Error() << "Unable to parse persistent property file: Could not parse protobuf";
}
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
const std::string temp_filename = persistent_property_filename + ".tmp";
unique_fd fd(TEMP_FAILURE_RETRY(
open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
@@ -191,7 +191,7 @@
unlink(temp_filename.c_str());
return Error(saved_errno) << "Unable to rename persistent property file";
}
- return Success();
+ return {};
}
// Persistent properties are not written often, so we rather not keep any data in memory and read
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index 5f4df85..3845a0d 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -30,7 +30,7 @@
// Exposed only for testing
Result<PersistentProperties> LoadPersistentPropertyFile();
-Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
extern std::string persistent_property_filename;
} // namespace init
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 872e9a1..13796a6 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -20,7 +20,7 @@
#include <vector>
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include "util.h"
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5328869..17622a3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,8 +39,12 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <atomic>
+#include <map>
#include <memory>
+#include <mutex>
#include <queue>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -49,15 +53,14 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <bootimg.h>
-#include <fs_mgr.h>
+#include <android-base/unique_fd.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
-#include "epoll.h"
+#include "debug_ramdisk.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
@@ -67,23 +70,25 @@
using namespace std::literals;
+using android::base::GetProperty;
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
using android::properties::PropertyInfoAreaFile;
using android::properties::PropertyInfoEntry;
-#define RECOVERY_MOUNT_POINT "/recovery"
-
namespace android {
namespace init {
+static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+
static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
@@ -101,7 +106,24 @@
const char* name;
};
+static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+ d->cr->gid);
+ return 0;
+}
+
void property_init() {
+ selinux_callback cb;
+ cb.func_audit = PropertyAuditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
+
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
if (__system_property_area_init()) {
@@ -152,13 +174,8 @@
return PROP_ERROR_INVALID_NAME;
}
- if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
- *error = "Property value too long";
- return PROP_ERROR_INVALID_VALUE;
- }
-
- if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
- *error = "Value is not a UTF8 encoded string";
+ if (auto result = IsLegalPropertyValue(name, value); !result) {
+ *error = result.error().message();
return PROP_ERROR_INVALID_VALUE;
}
@@ -188,88 +205,51 @@
return PROP_SUCCESS;
}
-typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+class AsyncRestorecon {
+ public:
+ void TriggerRestorecon(const std::string& path) {
+ auto guard = std::lock_guard{mutex_};
+ paths_.emplace(path);
-struct PropertyChildInfo {
- pid_t pid;
- PropertyAsyncFunc func;
- std::string name;
- std::string value;
+ if (!thread_started_) {
+ thread_started_ = true;
+ std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
+ }
+ }
+
+ private:
+ void ThreadFunction() {
+ auto lock = std::unique_lock{mutex_};
+
+ while (!paths_.empty()) {
+ auto path = paths_.front();
+ paths_.pop();
+
+ lock.unlock();
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+ LOG(ERROR) << "Asynchronous restorecon of '" << path << "' failed'";
+ }
+ android::base::SetProperty(kRestoreconProperty, path);
+ lock.lock();
+ }
+
+ thread_started_ = false;
+ }
+
+ std::mutex mutex_;
+ std::queue<std::string> paths_;
+ bool thread_started_ = false;
};
-static std::queue<PropertyChildInfo> property_children;
-
-static void PropertyChildLaunch() {
- auto& info = property_children.front();
- pid_t pid = fork();
- if (pid < 0) {
- LOG(ERROR) << "Failed to fork for property_set_async";
- while (!property_children.empty()) {
- property_children.pop();
- }
- return;
- }
- if (pid != 0) {
- info.pid = pid;
- } else {
- if (info.func(info.name, info.value) != 0) {
- LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
- << "\") failed";
- }
- _exit(0);
- }
-}
-
-bool PropertyChildReap(pid_t pid) {
- if (property_children.empty()) {
- return false;
- }
- auto& info = property_children.front();
- if (info.pid != pid) {
- return false;
- }
- std::string error;
- if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
- << error;
- }
- property_children.pop();
- if (!property_children.empty()) {
- PropertyChildLaunch();
- }
- return true;
-}
-
-static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
- PropertyAsyncFunc func, std::string* error) {
- if (value.empty()) {
- return PropertySet(name, value, error);
- }
-
- PropertyChildInfo info;
- info.func = func;
- info.name = name;
- info.value = value;
- property_children.push(info);
- if (property_children.size() == 1) {
- PropertyChildLaunch();
- }
- return PROP_SUCCESS;
-}
-
-static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
- return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-}
-
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
if (StartsWith(name, "ctl.")) {
LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
"functions directly";
return PROP_ERROR_INVALID_NAME;
}
- if (name == "selinux.restorecon_recursive") {
- LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
- "restorecon builtin directly";
+ if (name == kRestoreconProperty) {
+ LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
+ << "' from init; use the restorecon builtin directly";
return PROP_ERROR_INVALID_NAME;
}
@@ -330,18 +310,20 @@
return result == sizeof(value);
}
+ bool GetSourceContext(std::string* source_context) const {
+ char* c_source_context = nullptr;
+ if (getpeercon(socket_, &c_source_context) != 0) {
+ return false;
+ }
+ *source_context = c_source_context;
+ freecon(c_source_context);
+ return true;
+ }
+
int socket() { return socket_; }
const ucred& cred() { return cred_; }
- std::string source_context() const {
- char* source_context = nullptr;
- getpeercon(socket_, &source_context);
- std::string result = source_context;
- freecon(source_context);
- return result;
- }
-
private:
bool PollIn(uint32_t* timeout_ms) {
struct pollfd ufds[1];
@@ -445,8 +427,8 @@
}
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr, std::string* error) {
+uint32_t CheckPermissions(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error) {
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
@@ -459,7 +441,6 @@
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
- HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
@@ -478,6 +459,22 @@
return PROP_ERROR_INVALID_VALUE;
}
+ return PROP_SUCCESS;
+}
+
+// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error) {
+ if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
+ return ret;
+ }
+
+ if (StartsWith(name, "ctl.")) {
+ return HandleControlMessage(name.c_str() + 4, value, cr.pid)
+ ? PROP_SUCCESS
+ : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+ }
+
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {
@@ -493,8 +490,14 @@
<< process_log_string;
}
- if (name == "selinux.restorecon_recursive") {
- return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+ // If a process other than init is writing a non-empty value, it means that process is
+ // requesting that init performs a restorecon operation on the path specified by 'value'.
+ // We use a thread to do this restorecon operation to prevent holding up init, as it may take
+ // a long time to complete.
+ if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
+ static AsyncRestorecon async_restorecon;
+ async_restorecon.TriggerRestorecon(value);
+ return PROP_SUCCESS;
}
return PropertySet(name, value, error);
@@ -540,14 +543,18 @@
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result =
- HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
break;
@@ -563,13 +570,19 @@
return;
}
+ std::string source_context;
+ if (!socket.GetSourceContext(&source_context)) {
+ PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ return;
+ }
+
const auto& cr = socket.cred();
std::string error;
- uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+ uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
@@ -582,13 +595,15 @@
}
}
-static bool load_properties_from_file(const char *, const char *);
+static bool load_properties_from_file(const char*, const char*,
+ std::map<std::string, std::string>*);
/*
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void LoadProperties(char* data, const char* filter, const char* filename) {
+static void LoadProperties(char* data, const char* filter, const char* filename,
+ std::map<std::string, std::string>* properties) {
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
@@ -627,8 +642,15 @@
while (isspace(*key)) key++;
}
- load_properties_from_file(fn, key);
+ std::string raw_filename(fn);
+ auto expanded_filename = ExpandProps(raw_filename);
+ if (!expanded_filename) {
+ LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
+ continue;
+ }
+
+ load_properties_from_file(expanded_filename->c_str(), key, properties);
} else {
value = strchr(key, '=');
if (!value) continue;
@@ -641,25 +663,32 @@
if (flen > 0) {
if (filter[flen - 1] == '*') {
- if (strncmp(key, filter, flen - 1)) continue;
+ if (strncmp(key, filter, flen - 1) != 0) continue;
} else {
- if (strcmp(key, filter)) continue;
+ if (strcmp(key, filter) != 0) continue;
}
}
if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
- key == "selinux.restorecon_recursive"s) {
+ std::string{key} == kRestoreconProperty) {
LOG(ERROR) << "Ignoring disallowed property '" << key
<< "' with special meaning in prop file '" << filename << "'";
continue;
}
- uint32_t result = 0;
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
- result = HandlePropertySet(key, value, context, cr, &error);
- if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+ if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
+ auto it = properties->find(key);
+ if (it == properties->end()) {
+ (*properties)[key] = value;
+ } else if (it->second != value) {
+ LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
+ << it->second << "' with new value '" << value << "'";
+ it->second = value;
+ }
+ } else {
+ LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
<< "' in property file '" << filename << "': " << error;
}
}
@@ -668,7 +697,8 @@
// Filter is used to decide which properties to load: NULL loads all keys,
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter) {
+static bool load_properties_from_file(const char* filename, const char* filter,
+ std::map<std::string, std::string>* properties) {
Timer t;
auto file_contents = ReadFile(filename);
if (!file_contents) {
@@ -678,7 +708,7 @@
}
file_contents->push_back('\n');
- LoadProperties(file_contents->data(), filter, filename);
+ LoadProperties(file_contents->data(), filter, filename, properties);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
@@ -699,25 +729,17 @@
}
}
-void property_load_boot_defaults() {
- if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
- // Try recovery path
- if (!load_properties_from_file("/prop.default", NULL)) {
- // Try legacy path
- load_properties_from_file("/default.prop", NULL);
- }
- }
- load_properties_from_file("/product/build.prop", NULL);
- load_properties_from_file("/product_services/build.prop", NULL);
- load_properties_from_file("/odm/default.prop", NULL);
- load_properties_from_file("/vendor/default.prop", NULL);
-
- update_sys_usb_config();
-}
-
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
- load_properties_from_file("/data/local.prop", NULL);
+ std::map<std::string, std::string> properties;
+ load_properties_from_file("/data/local.prop", nullptr, &properties);
+ for (const auto& [name, value] : properties) {
+ std::string error;
+ if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Could not set '" << name << "' to '" << value
+ << "' in /data/local.prop: " << error;
+ }
+ }
}
}
@@ -748,56 +770,152 @@
property_set("ro.persistent_properties.ready", "true");
}
-void load_recovery_id_prop() {
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
- fs_mgr_free_fstab);
- if (!fstab) {
- PLOG(ERROR) << "unable to read default fstab";
- return;
- }
+// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
+// set, derive them from ro.product.${partition}.* properties
+static void property_initialize_ro_product_props() {
+ const char* RO_PRODUCT_PROPS_PREFIX = "ro.product.";
+ const char* RO_PRODUCT_PROPS[] = {
+ "brand", "device", "manufacturer", "model", "name",
+ };
+ const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
+ "odm", "product", "system_ext", "system", "vendor",
+ };
+ const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
+ const std::string EMPTY = "";
- fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), RECOVERY_MOUNT_POINT);
- if (rec == NULL) {
- LOG(ERROR) << "/recovery not specified in fstab";
- return;
- }
+ std::string ro_product_props_source_order =
+ GetProperty("ro.product.property_source_order", EMPTY);
- int fd = open(rec->blk_device, O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- PLOG(ERROR) << "error opening block device " << rec->blk_device;
- return;
- }
-
- boot_img_hdr hdr;
- if (android::base::ReadFully(fd, &hdr, sizeof(hdr))) {
- std::string hex = bytes_to_hex(reinterpret_cast<uint8_t*>(hdr.id), sizeof(hdr.id));
- property_set("ro.recovery_id", hex);
+ if (!ro_product_props_source_order.empty()) {
+ // Verify that all specified sources are valid
+ for (const auto& source : Split(ro_product_props_source_order, ",")) {
+ // Verify that the specified source is valid
+ bool is_allowed_source = false;
+ for (const auto& allowed_source : RO_PRODUCT_PROPS_ALLOWED_SOURCES) {
+ if (source == allowed_source) {
+ is_allowed_source = true;
+ break;
+ }
+ }
+ if (!is_allowed_source) {
+ LOG(ERROR) << "Found unexpected source in ro.product.property_source_order; "
+ "using the default property source order";
+ ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
+ break;
+ }
+ }
} else {
- PLOG(ERROR) << "error reading /recovery";
+ ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
}
- close(fd);
+ for (const auto& ro_product_prop : RO_PRODUCT_PROPS) {
+ std::string base_prop(RO_PRODUCT_PROPS_PREFIX);
+ base_prop += ro_product_prop;
+
+ std::string base_prop_val = GetProperty(base_prop, EMPTY);
+ if (!base_prop_val.empty()) {
+ continue;
+ }
+
+ for (const auto& source : Split(ro_product_props_source_order, ",")) {
+ std::string target_prop(RO_PRODUCT_PROPS_PREFIX);
+ target_prop += source;
+ target_prop += '.';
+ target_prop += ro_product_prop;
+
+ std::string target_prop_val = GetProperty(target_prop, EMPTY);
+ if (!target_prop_val.empty()) {
+ LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val
+ << "' (from " << target_prop << ")";
+ std::string error;
+ uint32_t res = PropertySet(base_prop, target_prop_val, &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res
+ << " (" << error << ")";
+ }
+ break;
+ }
+ }
+ }
}
-void load_system_props() {
- load_properties_from_file("/system/build.prop", NULL);
- load_properties_from_file("/odm/build.prop", NULL);
- load_properties_from_file("/vendor/build.prop", NULL);
- load_properties_from_file("/factory/factory.prop", "ro.*");
- load_recovery_id_prop();
-}
-
-static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- auto* d = reinterpret_cast<PropertyAuditData*>(data);
-
- if (!d || !d->name || !d->cr) {
- LOG(ERROR) << "AuditCallback invoked with null data arguments!";
- return 0;
+// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
+static void property_derive_build_fingerprint() {
+ std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
+ if (!build_fingerprint.empty()) {
+ return;
}
- snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
- d->cr->gid);
- return 0;
+ const std::string UNKNOWN = "unknown";
+ build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
+ build_fingerprint += '/';
+ build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
+ build_fingerprint += '/';
+ build_fingerprint += GetProperty("ro.product.device", UNKNOWN);
+ build_fingerprint += ':';
+ build_fingerprint += GetProperty("ro.build.version.release", UNKNOWN);
+ build_fingerprint += '/';
+ build_fingerprint += GetProperty("ro.build.id", UNKNOWN);
+ build_fingerprint += '/';
+ build_fingerprint += GetProperty("ro.build.version.incremental", UNKNOWN);
+ build_fingerprint += ':';
+ build_fingerprint += GetProperty("ro.build.type", UNKNOWN);
+ build_fingerprint += '/';
+ build_fingerprint += GetProperty("ro.build.tags", UNKNOWN);
+
+ LOG(INFO) << "Setting property 'ro.build.fingerprint' to '" << build_fingerprint << "'";
+
+ std::string error;
+ uint32_t res = PropertySet("ro.build.fingerprint", build_fingerprint, &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Error setting property 'ro.build.fingerprint': err=" << res << " (" << error
+ << ")";
+ }
+}
+
+void property_load_boot_defaults(bool load_debug_prop) {
+ // TODO(b/117892318): merge prop.default and build.prop files into one
+ // We read the properties and their values into a map, in order to always allow properties
+ // loaded in the later property files to override the properties in loaded in the earlier
+ // property files, regardless of if they are "ro." properties or not.
+ std::map<std::string, std::string> properties;
+ if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
+ // Try recovery path
+ if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
+ // Try legacy path
+ load_properties_from_file("/default.prop", nullptr, &properties);
+ }
+ }
+ load_properties_from_file("/system/build.prop", nullptr, &properties);
+ load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
+ load_properties_from_file("/vendor/default.prop", nullptr, &properties);
+ load_properties_from_file("/vendor/build.prop", nullptr, &properties);
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
+ load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
+ } else {
+ load_properties_from_file("/odm/default.prop", nullptr, &properties);
+ load_properties_from_file("/odm/build.prop", nullptr, &properties);
+ }
+ load_properties_from_file("/product/build.prop", nullptr, &properties);
+ load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
+
+ if (load_debug_prop) {
+ LOG(INFO) << "Loading " << kDebugRamdiskProp;
+ load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+ }
+
+ for (const auto& [name, value] : properties) {
+ std::string error;
+ if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Could not set '" << name << "' to '" << value
+ << "' while loading .prop files" << error;
+ }
+ }
+
+ property_initialize_ro_product_props();
+ property_derive_build_fingerprint();
+
+ update_sys_usb_config();
}
bool LoadPropertyInfoFromFile(const std::string& filename,
@@ -835,6 +953,13 @@
LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
&property_infos);
}
+ if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
+ &property_infos);
+ }
+ if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
+ }
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
@@ -843,6 +968,8 @@
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
+ LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
@@ -861,16 +988,13 @@
}
void StartPropertyService(Epoll* epoll) {
- selinux_callback cb;
- cb.func_audit = SelinuxAuditCallback;
- selinux_set_callback(SELINUX_CB_AUDIT, cb);
-
property_set("ro.property_service.version", "2");
- property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- false, 0666, 0, 0, nullptr);
- if (property_set_fd == -1) {
- PLOG(FATAL) << "start_property_service socket creation failed";
+ if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ false, 0666, 0, 0, {})) {
+ property_set_fd = *result;
+ } else {
+ PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
listen(property_set_fd, 8);
@@ -880,5 +1004,42 @@
}
}
+Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
+ unique_fd reader;
+ unique_fd writer;
+ if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
+ return ErrnoError() << "Could not create socket pair";
+ }
+
+ int result = 0;
+ std::atomic<bool> end = false;
+ auto thread = std::thread{[&f, &result, &end, &writer] {
+ result = f();
+ end = true;
+ send(writer, "1", 1, 0);
+ }};
+
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ return Error() << "Could not create epoll: " << result.error();
+ }
+ if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ return Error() << "Could not register epoll handler for property fd: " << result.error();
+ }
+
+ // No-op function, just used to break from loop.
+ if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
+ return Error() << "Could not register epoll handler for ending thread:" << result.error();
+ }
+
+ while (!end) {
+ epoll.Wait({});
+ }
+
+ thread.join();
+
+ return result;
+}
+
} // namespace init
} // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index 9022f5a..dc47b4d 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-#ifndef _INIT_PROPERTY_H
-#define _INIT_PROPERTY_H
+#pragma once
#include <sys/socket.h>
+#include <functional>
#include <string>
#include "epoll.h"
+#include "result.h"
namespace android {
namespace init {
@@ -33,15 +34,18 @@
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error);
-extern bool PropertyChildReap(pid_t pid);
-
-void property_init(void);
-void property_load_boot_defaults(void);
-void load_persist_props(void);
-void load_system_props(void);
+void property_init();
+void property_load_boot_defaults(bool load_debug_prop);
+void load_persist_props();
void StartPropertyService(Epoll* epoll);
+template <typename F, typename... Args>
+Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
+ Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
+
+ auto func = [&] { return f(args...); };
+ return CallFunctionAndHandlePropertiesImpl(func);
+}
+
} // namespace init
} // namespace android
-
-#endif /* _INIT_PROPERTY_H */
diff --git a/init/reboot.cpp b/init/reboot.cpp
index a145797..b0b5b54 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,11 +19,14 @@
#include <dirent.h>
#include <fcntl.h>
#include <linux/fs.h>
+#include <linux/loop.h>
#include <mntent.h>
+#include <semaphore.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/swap.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -38,7 +41,6 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
@@ -53,11 +55,16 @@
#include "property_service.h"
#include "reboot_utils.h"
#include "service.h"
+#include "service_list.h"
#include "sigchld_handler.h"
+#define PROC_SYSRQ "/proc/sysrq-trigger"
+
+using android::base::GetBoolProperty;
using android::base::Split;
-using android::base::StringPrintf;
using android::base::Timer;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
namespace android {
namespace init {
@@ -103,13 +110,17 @@
int st;
if (IsF2Fs()) {
const char* f2fs_argv[] = {
- "/system/bin/fsck.f2fs", "-f", mnt_fsname_.c_str(),
+ "/system/bin/fsck.f2fs",
+ "-a",
+ mnt_fsname_.c_str(),
};
android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
true, nullptr, nullptr, 0);
} else if (IsExt4()) {
const char* ext4_argv[] = {
- "/system/bin/e2fsck", "-f", "-y", mnt_fsname_.c_str(),
+ "/system/bin/e2fsck",
+ "-y",
+ mnt_fsname_.c_str(),
};
android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
true, nullptr, nullptr, 0);
@@ -189,7 +200,7 @@
return true;
}
-static void DumpUmountDebuggingInfo(bool dump_all) {
+static void DumpUmountDebuggingInfo() {
int status;
if (!security_getenforce()) {
LOG(INFO) << "Run lsof";
@@ -198,10 +209,9 @@
true, nullptr, nullptr, 0);
}
FindPartitionsToUmount(nullptr, nullptr, true);
- if (dump_all) {
- // dump current tasks, this log can be lengthy, so only dump with dump_all
- android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
- }
+ // dump current CPU stack traces and uninterruptible tasks
+ WriteStringToFile("l", PROC_SYSRQ);
+ WriteStringToFile("w", PROC_SYSRQ);
}
static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -241,7 +251,91 @@
}
}
-static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+static void KillAllProcesses() {
+ WriteStringToFile("i", PROC_SYSRQ);
+}
+
+// Create reboot/shutdwon monitor thread
+void RebootMonitorThread(unsigned int cmd, const std::string& rebootTarget, sem_t* reboot_semaphore,
+ std::chrono::milliseconds shutdown_timeout, bool* reboot_monitor_run) {
+ unsigned int remaining_shutdown_time = 0;
+
+ // 30 seconds more than the timeout passed to the thread as there is a final Umount pass
+ // after the timeout is reached.
+ constexpr unsigned int shutdown_watchdog_timeout_default = 30;
+ auto shutdown_watchdog_timeout = android::base::GetUintProperty(
+ "ro.build.shutdown.watchdog.timeout", shutdown_watchdog_timeout_default);
+ remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;
+
+ while (*reboot_monitor_run == true) {
+ if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {
+ LOG(ERROR) << "sem_wait failed and exit RebootMonitorThread()";
+ return;
+ }
+
+ timespec shutdown_timeout_timespec;
+ if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ // If there are some remaining shutdown time left from previous round, we use
+ // remaining time here.
+ shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;
+
+ LOG(INFO) << "shutdown_timeout_timespec.tv_sec: " << shutdown_timeout_timespec.tv_sec;
+
+ int sem_return = 0;
+ while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,
+ &shutdown_timeout_timespec)) == -1 &&
+ errno == EINTR) {
+ }
+
+ if (sem_return == -1) {
+ LOG(ERROR) << "Reboot thread timed out";
+
+ if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
+ LOG(INFO) << "Try to dump init process call trace:";
+ const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+ int status;
+ android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
+
+ LOG(INFO) << "Show stack for all active CPU:";
+ WriteStringToFile("l", PROC_SYSRQ);
+
+ LOG(INFO) << "Show tasks that are in disk sleep(uninterruptable sleep), which are "
+ "like "
+ "blocked in mutex or hardware register access:";
+ WriteStringToFile("w", PROC_SYSRQ);
+ }
+
+ // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.
+ if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+ WriteStringToFile("s", PROC_SYSRQ);
+
+ WriteStringToFile("u", PROC_SYSRQ);
+
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ LOG(ERROR) << "Trigger crash at last!";
+ WriteStringToFile("c", PROC_SYSRQ);
+ } else {
+ timespec current_time_timespec;
+
+ if (clock_gettime(CLOCK_MONOTONIC, ¤t_time_timespec) == -1) {
+ LOG(ERROR) << "clock_gettime() fail! exit RebootMonitorThread()";
+ return;
+ }
+
+ remaining_shutdown_time =
+ shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;
+
+ LOG(INFO) << "remaining_shutdown_time: " << remaining_shutdown_time;
+ }
+ }
+}
/* Try umounting all emulated file systems R/W block device cfile systems.
* This will just try umount and give it up if it fails.
@@ -252,7 +346,8 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
+static UmountStat TryUmountAndFsck(unsigned int cmd, const std::string& rebootTarget, bool runFsck,
+ std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -264,23 +359,71 @@
UmountStat stat = UmountPartitions(timeout - t.duration());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
UmountStat st = UmountPartitions(0ms);
- if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
+ if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ LOG(INFO) << "Pause reboot monitor thread before fsck";
+ sem_post(reboot_semaphore);
+
// fsck part is excluded from timeout check. It only runs for user initiated shutdown
// and should not affect reboot time.
for (auto& entry : block_devices) {
entry.DoFsck();
}
+
+ LOG(INFO) << "Resume reboot monitor thread after fsck";
+ sem_post(reboot_semaphore);
}
return stat;
}
+// zram is able to use backing device on top of a loopback device.
+// In order to unmount /data successfully, we have to kill the loopback device first
+#define ZRAM_DEVICE "/dev/block/zram0"
+#define ZRAM_RESET "/sys/block/zram0/reset"
+#define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
+static void KillZramBackingDevice() {
+ std::string backing_dev;
+ if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
+
+ if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
+
+ // cut the last "\n"
+ backing_dev.erase(backing_dev.length() - 1);
+
+ // shutdown zram handle
+ Timer swap_timer;
+ LOG(INFO) << "swapoff() start...";
+ if (swapoff(ZRAM_DEVICE) == -1) {
+ LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
+ return;
+ }
+ LOG(INFO) << "swapoff() took " << swap_timer;;
+
+ if (!WriteStringToFile("1", ZRAM_RESET)) {
+ LOG(ERROR) << "zram_backing_dev: reset (" << backing_dev << ")" << " failed";
+ return;
+ }
+
+ // clear loopback device
+ unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));
+ if (loop.get() < 0) {
+ LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
+ return;
+ }
+
+ if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
+ LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
+ return;
+ }
+ LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
+}
+
//* Reboot / shutdown the system.
// cmd ANDROID_RB_* as defined in android_reboot.h
// reason Reason string like "reboot", "shutdown,userrequested"
@@ -320,6 +463,23 @@
}
LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+ sem_t reboot_semaphore;
+ if (sem_init(&reboot_semaphore, false, 0) == -1) {
+ // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+ LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
+ RebootSystem(cmd, rebootTarget);
+ }
+
+ // Start a thread to monitor init shutdown process
+ LOG(INFO) << "Create reboot monitor thread.";
+ bool reboot_monitor_run = true;
+ std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, rebootTarget, &reboot_semaphore,
+ shutdown_timeout, &reboot_monitor_run);
+ reboot_monitor_thread.detach();
+
+ // Start reboot monitor thread
+ sem_post(&reboot_semaphore);
+
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -350,9 +510,31 @@
Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
- // will not check animation class separately
+ bool do_shutdown_animation = GetBoolProperty("ro.init.shutdown_animation", false);
+
+ if (do_shutdown_animation) {
+ property_set("service.bootanim.exit", "0");
+ // Could be in the middle of animation. Stop and start so that it can pick
+ // up the right mode.
+ bootAnim->Stop();
+ }
+
for (const auto& service : ServiceList::GetInstance()) {
- if (service->classnames().count("animation")) service->SetShutdownCritical();
+ if (service->classnames().count("animation") == 0) {
+ continue;
+ }
+
+ // start all animation classes if stopped.
+ if (do_shutdown_animation) {
+ service->Start();
+ }
+ service->SetShutdownCritical(); // will not check animation class separately
+ }
+
+ if (do_shutdown_animation) {
+ bootAnim->Start();
+ surfaceFlinger->SetShutdownCritical();
+ bootAnim->SetShutdownCritical();
}
}
@@ -423,7 +605,11 @@
sync();
LOG(INFO) << "sync() before umount took" << sync_timer;
}
- UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
+ // 5. drop caches and disable zram backing device, if exist
+ KillZramBackingDevice();
+
+ UmountStat stat = TryUmountAndFsck(cmd, rebootTarget, runFsck, shutdown_timeout - t.duration(),
+ &reboot_semaphore);
// Follow what linux shutdown is doing: one more sync with little bit delay
{
Timer sync_timer;
@@ -433,6 +619,11 @@
}
if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
+
+ // Send signal to terminate reboot monitor thread.
+ reboot_monitor_run = false;
+ sem_post(&reboot_semaphore);
+
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
@@ -445,11 +636,9 @@
bool run_fsck = false;
bool command_invalid = false;
- if (cmd_params.size() > 3) {
- command_invalid = true;
- } else if (cmd_params[0] == "shutdown") {
+ if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- if (cmd_params.size() == 2) {
+ if (cmd_params.size() >= 2) {
if (cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
@@ -468,7 +657,7 @@
// adb reboot fastboot should boot into bootloader for devices not
// supporting logical partitions.
if (reboot_target == "fastboot" &&
- !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+ !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
reboot_target = "bootloader";
}
// When rebooting to the bootloader notify the bootloader writing
@@ -480,6 +669,20 @@
"bootloader_message: "
<< err;
}
+ } else if (reboot_target == "recovery") {
+ bootloader_message boot = {};
+ if (std::string err; !read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << "Failed to read bootloader message: " << err;
+ }
+ // Update the boot command field if it's empty, and preserve
+ // the other arguments in the bootloader message.
+ if (boot.command[0] == '\0') {
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ if (std::string err; !write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return false;
+ }
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
@@ -495,9 +698,9 @@
reboot_target = "recovery";
}
- // If there is an additional parameter, pass it along
- if ((cmd_params.size() == 3) && cmd_params[2].size()) {
- reboot_target += "," + cmd_params[2];
+ // If there are additional parameter, pass them along
+ for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
+ reboot_target += "," + cmd_params[i];
}
}
} else {
@@ -515,7 +718,7 @@
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
DoReboot(cmd, command, reboot_target, run_fsck);
- return Success();
+ return Result<void>{};
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 9610304..d1a712f 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -19,14 +19,40 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include <android-base/logging.h>
-#include <cutils/android_reboot.h>
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/strings.h"
+#include "backtrace/Backtrace.h"
+#include "cutils/android_reboot.h"
#include "capabilities.h"
namespace android {
namespace init {
+static std::string init_fatal_reboot_target = "bootloader";
+
+void SetFatalRebootTarget() {
+ std::string cmdline;
+ android::base::ReadFileToString("/proc/cmdline", &cmdline);
+ cmdline = android::base::Trim(cmdline);
+
+ const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
+ auto start_pos = cmdline.find(kRebootTargetString);
+ if (start_pos == std::string::npos) {
+ return; // We already default to bootloader if no setting is provided.
+ }
+ start_pos += sizeof(kRebootTargetString) - 1;
+
+ auto end_pos = cmdline.find(' ', start_pos);
+ // if end_pos isn't found, then we've run off the end, but this is okay as this is the last
+ // entry, and -1 is a valid size for string::substr();
+ auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;
+ init_fatal_reboot_target = cmdline.substr(start_pos, size);
+}
+
bool IsRebootCapable() {
if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
@@ -75,6 +101,32 @@
abort();
}
+void __attribute__((noreturn)) InitFatalReboot() {
+ auto pid = fork();
+
+ if (pid == -1) {
+ // Couldn't fork, don't even try to backtrace, just reboot.
+ RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+ } else if (pid == 0) {
+ // Fork a child for safety, since we always want to shut down if something goes wrong, but
+ // its worth trying to get the backtrace, even in the signal handler, since typically it
+ // does work despite not being async-signal-safe.
+ sleep(5);
+ RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+ }
+
+ // In the parent, let's try to get a backtrace then shutdown.
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ if (!backtrace->Unwind(0)) {
+ LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+ }
+ for (size_t i = 0; i < backtrace->NumFrames(); i++) {
+ LOG(ERROR) << backtrace->FormatFrameData(i);
+ }
+ RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
+}
+
void InstallRebootSignalHandlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
@@ -94,7 +146,7 @@
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
// and probably good enough given this is already an error case and only enabled for
// development builds.
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ InitFatalReboot();
};
action.sa_flags = SA_RESTART;
sigaction(SIGABRT, &action, nullptr);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 073a16a..3fd969e 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -21,11 +21,13 @@
namespace android {
namespace init {
+void SetFatalRebootTarget();
// Determines whether the system is capable of rebooting. This is conservative,
// so if any of the attempts to determine this fail, it will still return true.
bool IsRebootCapable();
// This is a wrapper around the actual reboot calls.
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void __attribute__((noreturn)) InitFatalReboot();
void InstallRebootSignalHandlers();
} // namespace init
diff --git a/init/result.h b/init/result.h
index 0e3fd3d..b70dd1b 100644
--- a/init/result.h
+++ b/init/result.h
@@ -14,200 +14,16 @@
* limitations under the License.
*/
-// This file contains classes for returning a successful result along with an optional
-// arbitrarily typed return value or for returning a failure result along with an optional string
-// indicating why the function failed.
+#pragma once
-// There are 3 classes that implement this functionality and one additional helper type.
-//
-// Result<T> either contains a member of type T that can be accessed using similar semantics as
-// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
-// Result<T>::error().
-//
-// ResultError is a type that contains both a std::string describing the error and a copy of errno
-// from when the error occurred. ResultError can be used in an ostream directly to print its
-// string value.
-//
-// Success is a typedef that aids in creating Result<T> that do not contain a return value.
-// Result<Success> is the correct return type for a function that either returns successfully or
-// returns an error value. Returning Success() from a function that returns Result<Success> is the
-// correct way to indicate that a function without a return type has completed successfully.
-//
-// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
-// to T or from the constructor arguments for T. This allows you to return a type T directly from
-// a function that returns Result<T>.
-//
-// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
-// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
-// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
-// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
-// value can be directly specified via the Error() constructor.
-//
-// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
-// the string that the ResultError takes is passed through the stream normally, but the errno is
-// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
-// callers.
-//
-// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
-// function that return Result<T> but you have a Result<U> and want to return its error. In this
-// case, you can return the .error() from the Result<U> to construct the Result<T>.
+// The implementation of this file has moved to android-base. This file remains since historically,
+// these classes were a part of init.
-// An example of how to use these is below:
-// Result<U> CalculateResult(const T& input) {
-// U output;
-// if (!SomeOtherCppFunction(input, &output)) {
-// return Error() << "SomeOtherCppFunction(" << input << ") failed";
-// }
-// if (!c_api_function(output)) {
-// return ErrnoError() << "c_api_function(" << output << ") failed";
-// }
-// return output;
-// }
-//
-// auto output = CalculateResult(input);
-// if (!output) return Error() << "CalculateResult failed: " << output.error();
-// UseOutput(*output);
+#include <android-base/result.h>
-#ifndef _INIT_RESULT_H
-#define _INIT_RESULT_H
-
-#include <errno.h>
-
-#include <sstream>
-#include <string>
-#include <variant>
-
-namespace android {
-namespace init {
-
-struct ResultError {
- template <typename T>
- ResultError(T&& error_string, int error_errno)
- : error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
-
- std::string error_string;
- int error_errno;
-};
-
-inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
- os << t.error_string;
- return os;
-}
-
-inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
- os << std::move(t.error_string);
- return os;
-}
-
-class Error {
- public:
- Error() : errno_(0), append_errno_(false) {}
- Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
-
- template <typename T>
- Error&& operator<<(T&& t) {
- ss_ << std::forward<T>(t);
- return std::move(*this);
- }
-
- Error&& operator<<(const ResultError& result_error) {
- ss_ << result_error.error_string;
- errno_ = result_error.error_errno;
- return std::move(*this);
- }
-
- Error&& operator<<(ResultError&& result_error) {
- ss_ << std::move(result_error.error_string);
- errno_ = result_error.error_errno;
- return std::move(*this);
- }
-
- const std::string str() const {
- std::string str = ss_.str();
- if (append_errno_) {
- if (str.empty()) {
- return strerror(errno_);
- }
- return str + ": " + strerror(errno_);
- }
- return str;
- }
-
- int get_errno() const { return errno_; }
-
- Error(const Error&) = delete;
- Error(Error&&) = delete;
- Error& operator=(const Error&) = delete;
- Error& operator=(Error&&) = delete;
-
- private:
- std::stringstream ss_;
- int errno_;
- bool append_errno_;
-};
-
-inline Error ErrnoError() {
- return Error(errno);
-}
-
-template <typename T>
-class [[nodiscard]] Result {
- public:
- Result() {}
-
- template <typename U, typename... V,
- typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
- sizeof...(V) == 0)>>
- Result(U&& result, V&&... results)
- : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
- std::forward<V>(results)...) {}
-
- Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
- Result(const ResultError& result_error)
- : contents_(std::in_place_index_t<1>(), result_error.error_string,
- result_error.error_errno) {}
- Result(ResultError&& result_error)
- : contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
- result_error.error_errno) {}
-
- void IgnoreError() const {}
-
- bool has_value() const { return contents_.index() == 0; }
-
- T& value() & { return std::get<0>(contents_); }
- const T& value() const & { return std::get<0>(contents_); }
- T&& value() && { return std::get<0>(std::move(contents_)); }
- const T&& value() const && { return std::get<0>(std::move(contents_)); }
-
- const ResultError& error() const & { return std::get<1>(contents_); }
- ResultError&& error() && { return std::get<1>(std::move(contents_)); }
- const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
-
- const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
- std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
- const std::string&& error_string() const && {
- return std::get<1>(std::move(contents_)).error_string;
- }
-
- int error_errno() const { return std::get<1>(contents_).error_errno; }
-
- explicit operator bool() const { return has_value(); }
-
- T& operator*() & { return value(); }
- const T& operator*() const & { return value(); }
- T&& operator*() && { return std::move(value()); }
- const T&& operator*() const && { return std::move(value()); }
-
- T* operator->() { return &value(); }
- const T* operator->() const { return &value(); }
-
- private:
- std::variant<T, ResultError> contents_;
-};
-
-using Success = std::monostate;
-
-} // namespace init
-} // namespace android
-
-#endif
+using android::base::ErrnoError;
+using android::base::ErrnoErrorf;
+using android::base::Error;
+using android::base::Errorf;
+using android::base::Result;
+using android::base::ResultError;
diff --git a/init/result_test.cpp b/init/result_test.cpp
deleted file mode 100644
index 327b444..0000000
--- a/init/result_test.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "result.h"
-
-#include "errno.h"
-
-#include <string>
-
-#include <gtest/gtest.h>
-
-using namespace std::string_literals;
-
-namespace android {
-namespace init {
-
-TEST(result, result_accessors) {
- Result<std::string> result = "success";
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("success", *result);
- EXPECT_EQ("success", result.value());
-
- EXPECT_EQ('s', result->data()[0]);
-}
-
-TEST(result, result_accessors_rvalue) {
- ASSERT_TRUE(Result<std::string>("success"));
- ASSERT_TRUE(Result<std::string>("success").has_value());
-
- EXPECT_EQ("success", *Result<std::string>("success"));
- EXPECT_EQ("success", Result<std::string>("success").value());
-
- EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
-}
-
-TEST(result, result_success) {
- Result<Success> result = Success();
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ(Success(), *result);
- EXPECT_EQ(Success(), result.value());
-}
-
-TEST(result, result_success_rvalue) {
- // Success() doesn't actually create a Result<Success> object, but rather an object that can be
- // implicitly constructed into a Result<Success> object.
-
- auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
- ASSERT_TRUE(MakeRvalueSuccessResult());
- ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
-
- EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
- EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
-}
-
-TEST(result, result_error) {
- Result<Success> result = Error() << "failure" << 1;
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ("failure1", result.error_string());
-}
-
-TEST(result, result_error_empty) {
- Result<Success> result = Error();
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ("", result.error_string());
-}
-
-TEST(result, result_error_rvalue) {
- // Error() and ErrnoError() aren't actually used to create a Result<T> object.
- // Under the hood, they are an intermediate class that can be implicitly constructed into a
- // Result<T>. This is needed both to create the ostream and because Error() itself, by
- // definition will not know what the type, T, of the underlying Result<T> object that it would
- // create is.
-
- auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
- ASSERT_FALSE(MakeRvalueErrorResult());
- ASSERT_FALSE(MakeRvalueErrorResult().has_value());
-
- EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
- EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
-}
-
-TEST(result, result_errno_error) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<Success> result = ErrnoError() << "failure" << 1;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
-}
-
-TEST(result, result_errno_error_no_text) {
- constexpr int test_errno = 6;
- errno = test_errno;
- Result<Success> result = ErrnoError();
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ(strerror(test_errno), result.error_string());
-}
-
-TEST(result, result_error_from_other_result) {
- auto error_text = "test error"s;
- Result<Success> result = Error() << error_text;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ(error_text, result.error_string());
-}
-
-TEST(result, result_error_through_ostream) {
- auto error_text = "test error"s;
- Result<Success> result = Error() << error_text;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(0, result.error_errno());
- EXPECT_EQ(error_text, result.error_string());
-}
-
-TEST(result, result_errno_error_through_ostream) {
- auto error_text = "test error"s;
- constexpr int test_errno = 6;
- errno = 6;
- Result<Success> result = ErrnoError() << error_text;
-
- errno = 0;
-
- ASSERT_FALSE(result);
- ASSERT_FALSE(result.has_value());
-
- Result<std::string> result2 = Error() << result.error();
-
- ASSERT_FALSE(result2);
- ASSERT_FALSE(result2.has_value());
-
- EXPECT_EQ(test_errno, result.error_errno());
- EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
-}
-
-TEST(result, constructor_forwarding) {
- auto result = Result<std::string>(5, 'a');
-
- ASSERT_TRUE(result);
- ASSERT_TRUE(result.has_value());
-
- EXPECT_EQ("aaaaa", *result);
-}
-
-struct ConstructorTracker {
- static size_t constructor_called;
- static size_t copy_constructor_called;
- static size_t move_constructor_called;
- static size_t copy_assignment_called;
- static size_t move_assignment_called;
-
- template <typename T>
- ConstructorTracker(T&& string) : string(string) {
- ++constructor_called;
- }
-
- ConstructorTracker(const ConstructorTracker& ct) {
- ++copy_constructor_called;
- string = ct.string;
- }
- ConstructorTracker(ConstructorTracker&& ct) noexcept {
- ++move_constructor_called;
- string = std::move(ct.string);
- }
- ConstructorTracker& operator=(const ConstructorTracker& ct) {
- ++copy_assignment_called;
- string = ct.string;
- return *this;
- }
- ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
- ++move_assignment_called;
- string = std::move(ct.string);
- return *this;
- }
-
- std::string string;
-};
-
-size_t ConstructorTracker::constructor_called = 0;
-size_t ConstructorTracker::copy_constructor_called = 0;
-size_t ConstructorTracker::move_constructor_called = 0;
-size_t ConstructorTracker::copy_assignment_called = 0;
-size_t ConstructorTracker::move_assignment_called = 0;
-
-Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
- if (in.empty()) {
- return "literal string";
- }
- if (in == "test2") {
- return ConstructorTracker(in + in + "2");
- }
- ConstructorTracker result(in + " " + in);
- return result;
-};
-
-TEST(result, no_copy_on_return) {
- // If returning parameters that may be used to implicitly construct the type T of Result<T>,
- // then those parameters are forwarded to the construction of Result<T>.
-
- // If returning an prvalue or xvalue, it will be move constructed during the construction of
- // Result<T>.
-
- // This check ensures that that is the case, and particularly that no copy constructors
- // are called.
-
- auto result1 = ReturnConstructorTracker("");
- ASSERT_TRUE(result1);
- EXPECT_EQ("literal string", result1->string);
- EXPECT_EQ(1U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result2 = ReturnConstructorTracker("test2");
- ASSERT_TRUE(result2);
- EXPECT_EQ("test2test22", result2->string);
- EXPECT_EQ(2U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-
- auto result3 = ReturnConstructorTracker("test3");
- ASSERT_TRUE(result3);
- EXPECT_EQ("test3 test3", result3->string);
- EXPECT_EQ(3U, ConstructorTracker::constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
- EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
- EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
- EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
-}
-
-// Below two tests require that we do not hide the move constructor with our forwarding reference
-// constructor. This is done with by disabling the forwarding reference constructor if its first
-// and only type is Result<T>.
-TEST(result, result_result_with_success) {
- auto return_result_result_with_success = []() -> Result<Result<Success>> {
- return Result<Success>();
- };
- auto result = return_result_result_with_success();
- ASSERT_TRUE(result);
- ASSERT_TRUE(*result);
-
- auto inner_result = result.value();
- ASSERT_TRUE(inner_result);
-}
-
-TEST(result, result_result_with_failure) {
- auto return_result_result_with_error = []() -> Result<Result<Success>> {
- return Result<Success>(ResultError("failure string", 6));
- };
- auto result = return_result_result_with_error();
- ASSERT_TRUE(result);
- ASSERT_FALSE(*result);
- EXPECT_EQ("failure string", result->error_string());
- EXPECT_EQ(6, result->error_errno());
-}
-
-// This test requires that we disable the forwarding reference constructor if Result<T> is the
-// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
-// construct a Result<T>, then we still need the constructor.
-TEST(result, result_two_parameter_constructor_same_type) {
- struct TestStruct {
- TestStruct(int value) : value_(value) {}
- TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
- int value_;
- };
-
- auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
-
- auto result = return_test_struct();
- ASSERT_TRUE(result);
- EXPECT_EQ(36, result->value_);
-}
-
-TEST(result, die_on_access_failed_result) {
- Result<std::string> result = Error();
- ASSERT_DEATH(*result, "");
-}
-
-TEST(result, die_on_get_error_succesful_result) {
- Result<std::string> result = "success";
- ASSERT_DEATH(result.error_string(), "");
-}
-
-} // namespace init
-} // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index 1e0754a..476a46a 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -77,7 +77,7 @@
return Error() << "Could not parse hard limit '" << args[3] << "'";
}
- return {resource, limit};
+ return std::pair{resource, limit};
}
} // namespace init
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 659ba8a..6a16d3b 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -43,8 +43,8 @@
auto result = ParseRlimit(input);
ASSERT_FALSE(result) << "input: " << input[1];
- EXPECT_EQ(expected_result, result.error_string());
- EXPECT_EQ(0, result.error_errno());
+ EXPECT_EQ(expected_result, result.error().message());
+ EXPECT_EQ(0, result.error().code());
}
TEST(rlimit, RlimitSuccess) {
diff --git a/init/security.cpp b/init/security.cpp
index a3494a2..586d0c7 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -43,14 +43,14 @@
// devices/configurations where these I/O operations are blocking for a long
// time. We do not reboot or halt on failures, as this is a best-effort
// attempt.
-Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
unique_fd hwrandom_fd(
TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (hwrandom_fd == -1) {
if (errno == ENOENT) {
LOG(INFO) << "/dev/hw_random not found";
// It's not an error to not have a Hardware RNG.
- return Success();
+ return {};
}
return ErrnoError() << "Failed to open /dev/hw_random";
}
@@ -80,10 +80,10 @@
}
LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
- return Success();
+ return {};
}
-static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
std::ifstream inf(path, std::fstream::in);
if (!inf) {
LOG(ERROR) << "Cannot open for reading: " << path;
@@ -147,31 +147,31 @@
// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
// ec9ee4acd97c drivers: char: random: add get_random_long()
// 5ef11c35ce86 mm: ASLR: use get_random_long()
-Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&) {
// values are arch-dependent
#if defined(USER_MODE_LINUX)
// uml does not support mmap_rnd_bits
- return Success();
+ return {};
#elif defined(__aarch64__)
// arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
- return Success();
+ return {};
}
#elif defined(__x86_64__)
// x86_64 supports 28 - 32 bits
if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
- return Success();
+ return {};
}
#elif defined(__arm__) || defined(__i386__)
// check to see if we're running on 64-bit kernel
bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
// supported 32-bit architecture must have 16 bits set
if (SetMmapRndBitsMin(16, 16, h64)) {
- return Success();
+ return {};
}
#elif defined(__mips__) || defined(__mips64__)
// TODO: add mips support b/27788820
- return Success();
+ return {};
#else
LOG(ERROR) << "Unknown architecture";
#endif
@@ -187,14 +187,14 @@
// Set kptr_restrict to the highest available level.
//
// Aborts if unable to set this to an acceptable value.
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
+Result<void> SetKptrRestrictAction(const BuiltinArguments&) {
std::string path = KPTR_RESTRICT_PATH;
if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
return Error();
}
- return Success();
+ return {};
}
} // namespace init
diff --git a/init/security.h b/init/security.h
index 6f6b944..b081a05 100644
--- a/init/security.h
+++ b/init/security.h
@@ -26,9 +26,9 @@
namespace android {
namespace init {
-Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
-Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
-Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
+Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<void> SetKptrRestrictAction(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/init/selabel.cpp b/init/selabel.cpp
new file mode 100644
index 0000000..daeb832
--- /dev/null
+++ b/init/selabel.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "selabel.h"
+
+#include <selinux/android.h>
+
+namespace android {
+namespace init {
+
+namespace {
+
+selabel_handle* sehandle = nullptr;
+}
+
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ char* context;
+ if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result) {
+ result->clear();
+
+ if (!sehandle) return true;
+
+ std::vector<const char*> c_aliases;
+ for (const auto& alias : aliases) {
+ c_aliases.emplace_back(alias.c_str());
+ }
+ c_aliases.emplace_back(nullptr);
+
+ char* context;
+ if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+ return false;
+ }
+ *result = context;
+ free(context);
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/selabel.h b/init/selabel.h
new file mode 100644
index 0000000..5d590b2
--- /dev/null
+++ b/init/selabel.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+ const std::vector<std::string>& aliases, int type,
+ std::string* result);
+
+} // namespace init
+} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd7e86f..143cdfd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -18,8 +18,8 @@
// for SELinux operation for init.
// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
-// Init loads the SEPolicy from the file system, restores the context of /init based on this
-// SEPolicy, and finally exec()'s itself to run in the proper domain.
+// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on
+// this SEPolicy, and finally exec()'s itself to run in the proper domain.
// The SEPolicy on Android comes in two variants: monolithic and split.
@@ -34,16 +34,18 @@
// identical to the system image shipped on a vendor's device.
// The split SEPolicy is loaded as described below:
-// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
-// Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
-// were used to compile this precompiled policy. The system partition contains a similar sha256
-// of the parts of the SEPolicy that it currently contains. If these two hashes match, then the
-// system loads this precompiled_sepolicy directly.
-// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
-// init needs to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it
-// is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
-// and load it. That function contains even more documentation with the specific implementation
-// details of how the SEPolicy is compiled if needed.
+// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
+// /odm/etc/selinux/precompiled_sepolicy if odm parition is present. Stored along with this file
+// are the sha256 hashes of the parts of the SEPolicy on /system and /product that were used to
+// compile this precompiled policy. The system partition contains a similar sha256 of the parts
+// of the SEPolicy that it currently contains. Symmetrically, product paritition contains a
+// sha256 of its SEPolicy. System loads this precompiled_sepolicy directly if and only if hashes
+// for system policy match and hashes for product policy match.
+// 2) If these hashes do not match, then either /system or /product (or both) have been updated out
+// of sync with /vendor and the init needs to compile the SEPolicy. /system contains the
+// SEPolicy compiler, secilc, and it is used by the LoadSplitPolicy() function below to compile
+// the SEPolicy to a temp directory and load it. That function contains even more documentation
+// with the specific implementation details of how the SEPolicy is compiled if needed.
#include "selinux.h"
@@ -58,21 +60,25 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
+#include <fs_avb/fs_avb.h>
#include <selinux/android.h>
+#include "debug_ramdisk.h"
+#include "reboot_utils.h"
#include "util.h"
+using namespace std::string_literals;
+
using android::base::ParseInt;
using android::base::Timer;
using android::base::unique_fd;
+using android::fs_mgr::AvbHandle;
namespace android {
namespace init {
namespace {
-selabel_handle* sehandle = nullptr;
-
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
EnforcingStatus StatusFromCmdline() {
@@ -215,20 +221,35 @@
return false;
}
std::string actual_plat_id;
- if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
+ if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
PLOG(INFO) << "Failed to read "
- "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
+ "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
+ return false;
+ }
+ std::string actual_product_id;
+ if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
+ &actual_product_id)) {
+ PLOG(INFO) << "Failed to read "
+ "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
return false;
}
std::string precompiled_plat_id;
- std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
- if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
- PLOG(INFO) << "Failed to read " << precompiled_sha256;
+ std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
file->clear();
return false;
}
- if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+ std::string precompiled_product_id;
+ std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
+ file->clear();
+ return false;
+ }
+ if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
+ actual_product_id.empty() || actual_product_id != precompiled_product_id) {
file->clear();
return false;
}
@@ -263,10 +284,21 @@
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
+ // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
+ const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
+ bool use_userdebug_policy =
+ ((force_debuggable_env && "true"s == force_debuggable_env) &&
+ AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
+ if (use_userdebug_policy) {
+ LOG(WARNING) << "Using userdebug system sepolicy";
+ }
+
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
// must match the platform policy on the system image.
std::string precompiled_sepolicy_file;
- if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
+ // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
+ // Thus it cannot use the precompiled policy from vendor image.
+ if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
if (fd != -1) {
if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
@@ -280,14 +312,6 @@
LOG(INFO) << "Compiling SELinux policy";
- // Determine the highest policy language version supported by the kernel
- set_selinuxmnt("/sys/fs/selinux");
- int max_policy_version = security_policyvers();
- if (max_policy_version == -1) {
- PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
- return false;
- }
-
// We store the output of the compilation on /dev because this is the most convenient tmpfs
// storage mount available this early in the boot sequence.
char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
@@ -302,7 +326,23 @@
if (!GetVendorMappingVersion(&vend_plat_vers)) {
return false;
}
- std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+ std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
+ ".compat.cil");
+ if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
+ plat_compat_cil_file.clear();
+ }
+
+ std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
+ if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
+ product_policy_cil_file.clear();
+ }
+
+ std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+ if (access(product_mapping_file.c_str(), F_OK) == -1) {
+ product_mapping_file.clear();
+ }
// vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
// nonplat_sepolicy.cil.
@@ -324,22 +364,30 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
- const std::string version_as_string = std::to_string(max_policy_version);
+ const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
std::vector<const char*> compile_args {
"/system/bin/secilc",
- plat_policy_cil_file,
+ use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
"-m", "-M", "true", "-G", "-N",
- // Target the highest policy language version supported by the kernel
"-c", version_as_string.c_str(),
- mapping_file.c_str(),
+ plat_mapping_file.c_str(),
"-o", compiled_sepolicy,
// We don't care about file_contexts output by the compiler
"-f", "/sys/fs/selinux/null", // /dev/null is not yet available
};
// clang-format on
+ if (!plat_compat_cil_file.empty()) {
+ compile_args.push_back(plat_compat_cil_file.c_str());
+ }
+ if (!product_policy_cil_file.empty()) {
+ compile_args.push_back(product_policy_cil_file.c_str());
+ }
+ if (!product_mapping_file.empty()) {
+ compile_args.push_back(product_mapping_file.c_str());
+ }
if (!plat_pub_versioned_cil_file.empty()) {
compile_args.push_back(plat_pub_versioned_cil_file.c_str());
}
@@ -379,11 +427,7 @@
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
-} // namespace
-
void SelinuxInitialize() {
- Timer t;
-
LOG(INFO) << "Loading SELinux policy";
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
@@ -400,11 +444,10 @@
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
}
-
- // init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
+} // namespace
+
// The files and directories that were created before initial sepolicy load or
// files on ramdisk need to have their security context restored to the proper
// value. This must happen before /dev is populated by ueventd.
@@ -422,32 +465,10 @@
selinux_android_restorecon("/dev/urandom", 0);
selinux_android_restorecon("/dev/__properties__", 0);
- selinux_android_restorecon("/plat_file_contexts", 0);
- selinux_android_restorecon("/nonplat_file_contexts", 0);
- selinux_android_restorecon("/vendor_file_contexts", 0);
- selinux_android_restorecon("/plat_property_contexts", 0);
- selinux_android_restorecon("/nonplat_property_contexts", 0);
- selinux_android_restorecon("/vendor_property_contexts", 0);
- selinux_android_restorecon("/plat_seapp_contexts", 0);
- selinux_android_restorecon("/nonplat_seapp_contexts", 0);
- selinux_android_restorecon("/vendor_seapp_contexts", 0);
- selinux_android_restorecon("/plat_service_contexts", 0);
- selinux_android_restorecon("/nonplat_service_contexts", 0);
- selinux_android_restorecon("/vendor_service_contexts", 0);
- selinux_android_restorecon("/plat_hwservice_contexts", 0);
- selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
- selinux_android_restorecon("/vendor_hwservice_contexts", 0);
- selinux_android_restorecon("/sepolicy", 0);
- selinux_android_restorecon("/vndservice_contexts", 0);
-
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
selinux_android_restorecon("/dev/device-mapper", 0);
- selinux_android_restorecon("/sbin/mke2fs_static", 0);
- selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
-
- selinux_android_restorecon("/sbin/mkfs.f2fs", 0);
- selinux_android_restorecon("/sbin/sload.f2fs", 0);
+ selinux_android_restorecon("/apex", 0);
}
int SelinuxKlogCallback(int type, const char* fmt, ...) {
@@ -476,73 +497,63 @@
// This function returns the Android version with which the vendor SEPolicy was compiled.
// It is used for version checks such as whether or not vendor_init should be used
int SelinuxGetVendorAndroidVersion() {
- if (!IsSplitPolicyDevice()) {
- // If this device does not split sepolicy files, it's not a Treble device and therefore,
- // we assume it's always on the latest platform.
- return __ANDROID_API_FUTURE__;
- }
+ static int vendor_android_version = [] {
+ if (!IsSplitPolicyDevice()) {
+ // If this device does not split sepolicy files, it's not a Treble device and therefore,
+ // we assume it's always on the latest platform.
+ return __ANDROID_API_FUTURE__;
+ }
- std::string version;
- if (!GetVendorMappingVersion(&version)) {
- LOG(FATAL) << "Could not read vendor SELinux version";
- }
+ std::string version;
+ if (!GetVendorMappingVersion(&version)) {
+ LOG(FATAL) << "Could not read vendor SELinux version";
+ }
- int major_version;
- std::string major_version_str(version, 0, version.find('.'));
- if (!ParseInt(major_version_str, &major_version)) {
- PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
- }
+ int major_version;
+ std::string major_version_str(version, 0, version.find('.'));
+ if (!ParseInt(major_version_str, &major_version)) {
+ PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "
+ << major_version_str;
+ }
- return major_version;
+ return major_version;
+ }();
+ return vendor_android_version;
}
-// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
-// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
-// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
-// one, thus eliminating an extra call to selinux_android_file_context_handle().
-void SelabelInitialize() {
- sehandle = selinux_android_file_context_handle();
- selinux_android_set_sehandle(sehandle);
-}
+// This function initializes SELinux then execs init to run in the init SELinux context.
+int SetupSelinux(char** argv) {
+ InitKernelLogging(argv);
-// A C++ wrapper around selabel_lookup() using the cached sehandle.
-// If sehandle is null, this returns success with an empty context.
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
- result->clear();
-
- if (!sehandle) return true;
-
- char* context;
- if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
- return false;
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
}
- *result = context;
- free(context);
- return true;
-}
-// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
-// If sehandle is null, this returns success with an empty context.
-bool SelabelLookupFileContextBestMatch(const std::string& key,
- const std::vector<std::string>& aliases, int type,
- std::string* result) {
- result->clear();
+ boot_clock::time_point start_time = boot_clock::now();
- if (!sehandle) return true;
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
- std::vector<const char*> c_aliases;
- for (const auto& alias : aliases) {
- c_aliases.emplace_back(alias.c_str());
+ // We're in the kernel domain and want to transition to the init domain. File systems that
+ // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+ // but other file systems do. In particular, this is needed for ramdisks such as the
+ // recovery image for A/B devices.
+ if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
- c_aliases.emplace_back(nullptr);
- char* context;
- if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
- return false;
- }
- *result = context;
- free(context);
- return true;
+ setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "second_stage", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
}
} // namespace init
diff --git a/init/selinux.h b/init/selinux.h
index c41d7f0..63ad470 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -14,28 +14,18 @@
* limitations under the License.
*/
-#ifndef _INIT_SELINUX_H
-#define _INIT_SELINUX_H
-
-#include <string>
-#include <vector>
+#pragma once
namespace android {
namespace init {
-void SelinuxInitialize();
+int SetupSelinux(char** argv);
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
int SelinuxGetVendorAndroidVersion();
-void SelabelInitialize();
-bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
-bool SelabelLookupFileContextBestMatch(const std::string& key,
- const std::vector<std::string>& aliases, int type,
- std::string* result);
+static constexpr char kEnvSelinuxStartedAt[] = "SELINUX_STARTED_AT";
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/service.cpp b/init/service.cpp
index 7f49423..e60c20d 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,40 +18,31 @@
#include <fcntl.h>
#include <inttypes.h>
-#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
-#include <sys/mount.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
-#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <hidl-util/FQName.h>
+#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
-#include <system/thread_defs.h>
-#include "rlimit_parser.h"
+#include "service_list.h"
#include "util.h"
#if defined(__ANDROID__)
-#include <android/api-level.h>
-#include <sys/system_properties.h>
+#include <ApexProperties.sysprop.h>
-#include "init.h"
+#include "mount_namespace.h"
#include "property_service.h"
-#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
@@ -59,11 +50,8 @@
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
-using android::base::ParseInt;
-using android::base::Split;
using android::base::StartsWith;
using android::base::StringPrintf;
-using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
@@ -105,87 +93,6 @@
return computed_context;
}
-Result<Success> Service::SetUpMountNamespace() const {
- constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
-
- // Recursively remount / as slave like zygote does so unmounting and mounting /proc
- // doesn't interfere with the parent namespace's /proc mount. This will also
- // prevent any other mounts/unmounts initiated by the service from interfering
- // with the parent namespace but will still allow mount events from the parent
- // namespace to propagate to the child.
- if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as slave";
- }
-
- // umount() then mount() /proc and/or /sys
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (namespace_flags_ & CLONE_NEWPID) {
- if (umount("/proc") == -1) {
- return ErrnoError() << "Could not umount(/proc)";
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- return ErrnoError() << "Could not mount(/proc)";
- }
- }
- bool remount_sys = std::any_of(namespaces_to_enter_.begin(), namespaces_to_enter_.end(),
- [](const auto& entry) { return entry.first == CLONE_NEWNET; });
- if (remount_sys) {
- if (umount2("/sys", MNT_DETACH) == -1) {
- return ErrnoError() << "Could not umount(/sys)";
- }
- if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
- return ErrnoError() << "Could not mount(/sys)";
- }
- }
- return Success();
-}
-
-Result<Success> Service::SetUpPidNamespace() const {
- if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
- return ErrnoError() << "Could not set name";
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- return ErrnoError() << "Could not fork init inside the PID namespace";
- }
-
- if (child_pid > 0) {
- // So that we exit with the right status.
- static int init_exitstatus = 0;
- signal(SIGTERM, [](int) { _exit(init_exitstatus); });
-
- pid_t waited_pid;
- int status;
- while ((waited_pid = wait(&status)) > 0) {
- // This loop will end when there are no processes left inside the
- // PID namespace or when the init process inside the PID namespace
- // gets a signal.
- if (waited_pid == child_pid) {
- init_exitstatus = status;
- }
- }
- if (!WIFEXITED(init_exitstatus)) {
- _exit(EXIT_FAILURE);
- }
- _exit(WEXITSTATUS(init_exitstatus));
- }
- return Success();
-}
-
-Result<Success> Service::EnterNamespaces() const {
- for (const auto& [nstype, path] : namespaces_to_enter_) {
- auto fd = unique_fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
- if (!fd) {
- return ErrnoError() << "Could not open namespace at " << path;
- }
- if (setns(fd, nstype) == -1) {
- return ErrnoError() << "Could not setns() namespace at " << path;
- }
- }
- return Success();
-}
-
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
std::vector<std::string> expanded_args;
std::vector<char*> c_strings;
@@ -193,9 +100,11 @@
expanded_args.resize(args.size());
c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
- if (!expand_props(args[i], &expanded_args[i])) {
- LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+ auto expanded_arg = ExpandProps(args[i]);
+ if (!expanded_arg) {
+ LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
}
+ expanded_args[i] = *expanded_arg;
c_strings.push_back(expanded_args[i].data());
}
c_strings.push_back(nullptr);
@@ -207,37 +116,38 @@
return execv(c_strings[0], c_strings.data()) == 0;
}
+static bool IsRuntimeApexReady() {
+ struct stat buf;
+ return stat("/apex/com.android.runtime/", &buf) == 0;
+}
+
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::vector<std::string>& args)
- : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {}
+ : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
- const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
- unsigned namespace_flags, const std::string& seclabel,
- Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
+ const std::vector<gid_t>& supp_gids, int namespace_flags,
+ const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
+ const std::vector<std::string>& args)
: name_(name),
classnames_({"default"}),
flags_(flags),
pid_(0),
crash_count_(0),
- uid_(uid),
- gid_(gid),
- supp_gids_(supp_gids),
- capabilities_(capabilities),
- namespace_flags_(namespace_flags),
+ proc_attr_{.ioprio_class = IoSchedClass_NONE,
+ .ioprio_pri = 0,
+ .uid = uid,
+ .gid = gid,
+ .supp_gids = supp_gids,
+ .priority = 0},
+ namespaces_{.flags = namespace_flags},
seclabel_(seclabel),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
- ioprio_class_(IoSchedClass_NONE),
- ioprio_pri_(0),
- priority_(0),
oom_score_adjust_(-1000),
- swappiness_(-1),
- soft_limit_in_bytes_(-1),
- limit_in_bytes_(-1),
start_order_(0),
args_(args) {}
@@ -269,24 +179,18 @@
<< ") process group...";
int r;
if (signal == SIGTERM) {
- r = killProcessGroupOnce(uid_, pid_, signal);
+ r = killProcessGroupOnce(proc_attr_.uid, pid_, signal);
} else {
- r = killProcessGroup(uid_, pid_, signal);
+ r = killProcessGroup(proc_attr_.uid, pid_, signal);
}
if (r == 0) process_cgroup_empty_ = true;
}
}
-void Service::SetProcessAttributes() {
- for (const auto& rlimit : rlimits_) {
- if (setrlimit(rlimit.first, &rlimit.second) == -1) {
- LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
- rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
- }
- }
+void Service::SetProcessAttributesAndCaps() {
// Keep capabilites on uid change.
- if (capabilities_.any() && uid_) {
+ if (capabilities_ && proc_attr_.uid) {
// If Android is running in a container, some securebits might already
// be locked, so don't change those.
unsigned long securebits = prctl(PR_GET_SECUREBITS);
@@ -299,37 +203,21 @@
}
}
- // TODO: work out why this fails for `console` then upgrade to FATAL.
- if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
+ if (auto result = SetProcessAttributes(proc_attr_); !result) {
+ LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
+ }
- if (gid_) {
- if (setgid(gid_) != 0) {
- PLOG(FATAL) << "setgid failed for " << name_;
- }
- }
- if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
- PLOG(FATAL) << "setgroups failed for " << name_;
- }
- if (uid_) {
- if (setuid(uid_) != 0) {
- PLOG(FATAL) << "setuid failed for " << name_;
- }
- }
if (!seclabel_.empty()) {
if (setexeccon(seclabel_.c_str()) < 0) {
PLOG(FATAL) << "cannot setexeccon('" << seclabel_ << "') for " << name_;
}
}
- if (priority_ != 0) {
- if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
- PLOG(FATAL) << "setpriority failed for " << name_;
- }
- }
- if (capabilities_.any()) {
- if (!SetCapsForExec(capabilities_)) {
+
+ if (capabilities_) {
+ if (!SetCapsForExec(*capabilities_)) {
LOG(FATAL) << "cannot set capabilities for " << name_;
}
- } else if (uid_) {
+ } else if (proc_attr_.uid) {
// Inheritable caps can be non-zero when running in a container.
if (!DropInheritableCaps()) {
LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -342,9 +230,11 @@
KillProcessGroup(SIGKILL);
}
- // Remove any descriptor resources we may have created.
- std::for_each(descriptors_.begin(), descriptors_.end(),
- std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
+ // Remove any socket resources we may have created.
+ for (const auto& socket : sockets_) {
+ auto path = ANDROID_SOCKET_DIR "/" + socket.name;
+ unlink(path.c_str());
+ }
for (const auto& f : reap_callbacks_) {
f(siginfo);
@@ -360,7 +250,7 @@
// Oneshot processes go into the disabled state on exit,
// except when manually restarted.
- if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+ if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
flags_ |= SVC_DISABLED;
}
@@ -370,12 +260,30 @@
return;
}
- // If we crash > 4 times in 4 minutes, reboot into bootloader.
+#if defined(__ANDROID__)
+ static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+#else
+ static bool is_apex_updatable = false;
+#endif
+ const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
+
+ // If we crash > 4 times in 4 minutes or before boot_completed,
+ // reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
- if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
- if (now < time_crashed_ + 4min) {
+ if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+ bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
+ if (now < time_crashed_ + 4min || !boot_completed) {
if (++crash_count_ > 4) {
- LOG(FATAL) << "critical process '" << name_ << "' exited 4 times in 4 minutes";
+ if (flags_ & SVC_CRITICAL) {
+ // Aborts into bootloader
+ LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
+ } else {
+ LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
+ << (boot_completed ? "in 4 minutes" : "before boot completed");
+ // Notifies update_verifier and apexd
+ property_set("ro.init.updatable_crashing", "1");
+ }
}
} else {
time_crashed_ = now;
@@ -397,430 +305,23 @@
LOG(INFO) << "service " << name_;
LOG(INFO) << " class '" << Join(classnames_, " ") << "'";
LOG(INFO) << " exec " << Join(args_, " ");
- std::for_each(descriptors_.begin(), descriptors_.end(),
- [] (const auto& info) { LOG(INFO) << *info; });
+ for (const auto& socket : sockets_) {
+ LOG(INFO) << " socket " << socket.name;
+ }
+ for (const auto& file : files_) {
+ LOG(INFO) << " file " << file.name;
+ }
}
-Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
- capabilities_ = 0;
- if (!CapAmbientSupported()) {
- return Error()
- << "capabilities requested but the kernel does not support ambient capabilities";
+Result<void> Service::ExecStart() {
+ if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ // Don't delay the service for ExecStart() as the semantic is that
+ // the caller might depend on the side effect of the execution.
+ return Error() << "Cannot start an updatable service '" << name_
+ << "' before configs from APEXes are all loaded";
}
- unsigned int last_valid_cap = GetLastValidCap();
- if (last_valid_cap >= capabilities_.size()) {
- LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
- }
-
- for (size_t i = 1; i < args.size(); i++) {
- const std::string& arg = args[i];
- int res = LookupCap(arg);
- if (res < 0) {
- return Error() << StringPrintf("invalid capability '%s'", arg.c_str());
- }
- unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
- if (cap > last_valid_cap) {
- return Error() << StringPrintf("capability '%s' not supported by the kernel",
- arg.c_str());
- }
- capabilities_[cap] = true;
- }
- return Success();
-}
-
-Result<Success> Service::ParseClass(std::vector<std::string>&& args) {
- classnames_ = std::set<std::string>(args.begin() + 1, args.end());
- return Success();
-}
-
-Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
- flags_ |= SVC_CONSOLE;
- console_ = args.size() > 1 ? "/dev/" + args[1] : "";
- return Success();
-}
-
-Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
- flags_ |= SVC_CRITICAL;
- return Success();
-}
-
-Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
- flags_ |= SVC_DISABLED;
- flags_ |= SVC_RC_DISABLED;
- return Success();
-}
-
-Result<Success> Service::ParseEnterNamespace(std::vector<std::string>&& args) {
- if (args[1] != "net") {
- return Error() << "Init only supports entering network namespaces";
- }
- if (!namespaces_to_enter_.empty()) {
- return Error() << "Only one network namespace may be entered";
- }
- // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
- // present. Therefore, they also require mount namespaces.
- namespace_flags_ |= CLONE_NEWNS;
- namespaces_to_enter_.emplace_back(CLONE_NEWNET, std::move(args[2]));
- return Success();
-}
-
-Result<Success> Service::ParseGroup(std::vector<std::string>&& args) {
- auto gid = DecodeUid(args[1]);
- if (!gid) {
- return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
- }
- gid_ = *gid;
-
- for (std::size_t n = 2; n < args.size(); n++) {
- gid = DecodeUid(args[n]);
- if (!gid) {
- return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
- }
- supp_gids_.emplace_back(*gid);
- }
- return Success();
-}
-
-Result<Success> Service::ParsePriority(std::vector<std::string>&& args) {
- priority_ = 0;
- if (!ParseInt(args[1], &priority_,
- static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
- static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
- return Error() << StringPrintf("process priority value must be range %d - %d",
- ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
- }
- return Success();
-}
-
-Result<Success> Service::ParseInterface(std::vector<std::string>&& args) {
- const std::string& interface_name = args[1];
- const std::string& instance_name = args[2];
-
- FQName fq_name;
- if (!FQName::parse(interface_name, &fq_name)) {
- return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
- }
-
- if (!fq_name.isFullyQualified()) {
- return Error() << "Interface name not fully-qualified '" << interface_name << "'";
- }
-
- if (fq_name.isValidValueName()) {
- return Error() << "Interface name must not be a value name '" << interface_name << "'";
- }
-
- const std::string fullname = interface_name + "/" + instance_name;
-
- for (const auto& svc : ServiceList::GetInstance()) {
- if (svc->interfaces().count(fullname) > 0) {
- return Error() << "Interface '" << fullname << "' redefined in " << name()
- << " but is already defined by " << svc->name();
- }
- }
-
- interfaces_.insert(fullname);
-
- return Success();
-}
-
-Result<Success> Service::ParseIoprio(std::vector<std::string>&& args) {
- if (!ParseInt(args[2], &ioprio_pri_, 0, 7)) {
- return Error() << "priority value must be range 0 - 7";
- }
-
- if (args[1] == "rt") {
- ioprio_class_ = IoSchedClass_RT;
- } else if (args[1] == "be") {
- ioprio_class_ = IoSchedClass_BE;
- } else if (args[1] == "idle") {
- ioprio_class_ = IoSchedClass_IDLE;
- } else {
- return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
- }
-
- return Success();
-}
-
-Result<Success> Service::ParseKeycodes(std::vector<std::string>&& args) {
- auto it = args.begin() + 1;
- if (args.size() == 2 && StartsWith(args[1], "$")) {
- std::string expanded;
- if (!expand_props(args[1], &expanded)) {
- return Error() << "Could not expand property '" << args[1] << "'";
- }
-
- // If the property is not set, it defaults to none, in which case there are no keycodes
- // for this service.
- if (expanded == "none") {
- return Success();
- }
-
- args = Split(expanded, ",");
- it = args.begin();
- }
-
- for (; it != args.end(); ++it) {
- int code;
- if (ParseInt(*it, &code, 0, KEY_MAX)) {
- for (auto& key : keycodes_) {
- if (key == code) return Error() << "duplicate keycode: " << *it;
- }
- keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
- } else {
- return Error() << "invalid keycode: " << *it;
- }
- }
- return Success();
-}
-
-Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
- flags_ |= SVC_ONESHOT;
- return Success();
-}
-
-Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
- args.erase(args.begin());
- int line = onrestart_.NumCommands() + 1;
- if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
- return Error() << "cannot add Onrestart command: " << result.error();
- }
- return Success();
-}
-
-Result<Success> Service::ParseNamespace(std::vector<std::string>&& args) {
- for (size_t i = 1; i < args.size(); i++) {
- if (args[i] == "pid") {
- namespace_flags_ |= CLONE_NEWPID;
- // PID namespaces require mount namespaces.
- namespace_flags_ |= CLONE_NEWNS;
- } else if (args[i] == "mnt") {
- namespace_flags_ |= CLONE_NEWNS;
- } else {
- return Error() << "namespace must be 'pid' or 'mnt'";
- }
- }
- return Success();
-}
-
-Result<Success> Service::ParseOomScoreAdjust(std::vector<std::string>&& args) {
- if (!ParseInt(args[1], &oom_score_adjust_, -1000, 1000)) {
- return Error() << "oom_score_adjust value must be in range -1000 - +1000";
- }
- return Success();
-}
-
-Result<Success> Service::ParseOverride(std::vector<std::string>&& args) {
- override_ = true;
- return Success();
-}
-
-Result<Success> Service::ParseMemcgSwappiness(std::vector<std::string>&& args) {
- if (!ParseInt(args[1], &swappiness_, 0)) {
- return Error() << "swappiness value must be equal or greater than 0";
- }
- return Success();
-}
-
-Result<Success> Service::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
- if (!ParseInt(args[1], &limit_in_bytes_, 0)) {
- return Error() << "limit_in_bytes value must be equal or greater than 0";
- }
- return Success();
-}
-
-Result<Success> Service::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
- if (!ParseInt(args[1], &soft_limit_in_bytes_, 0)) {
- return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
- }
- return Success();
-}
-
-Result<Success> Service::ParseProcessRlimit(std::vector<std::string>&& args) {
- auto rlimit = ParseRlimit(args);
- if (!rlimit) return rlimit.error();
-
- rlimits_.emplace_back(*rlimit);
- return Success();
-}
-
-Result<Success> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
- int period;
- if (!ParseInt(args[1], &period, 5)) {
- return Error() << "restart_period value must be an integer >= 5";
- }
- restart_period_ = std::chrono::seconds(period);
- return Success();
-}
-
-Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
- seclabel_ = std::move(args[1]);
- return Success();
-}
-
-Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
- sigstop_ = true;
- return Success();
-}
-
-Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
- environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
- return Success();
-}
-
-Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
- if (args[1] == "critical") {
- flags_ |= SVC_SHUTDOWN_CRITICAL;
- return Success();
- }
- return Error() << "Invalid shutdown option";
-}
-
-Result<Success> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
- int period;
- if (!ParseInt(args[1], &period, 1)) {
- return Error() << "timeout_period value must be an integer >= 1";
- }
- timeout_period_ = std::chrono::seconds(period);
- return Success();
-}
-
-template <typename T>
-Result<Success> Service::AddDescriptor(std::vector<std::string>&& args) {
- int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
- Result<uid_t> uid = 0;
- Result<gid_t> gid = 0;
- std::string context = args.size() > 6 ? args[6] : "";
-
- if (args.size() > 4) {
- uid = DecodeUid(args[4]);
- if (!uid) {
- return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
- }
- }
-
- if (args.size() > 5) {
- gid = DecodeUid(args[5]);
- if (!gid) {
- return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
- }
- }
-
- auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
-
- auto old =
- std::find_if(descriptors_.begin(), descriptors_.end(),
- [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
-
- if (old != descriptors_.end()) {
- return Error() << "duplicate descriptor " << args[1] << " " << args[2];
- }
-
- descriptors_.emplace_back(std::move(descriptor));
- return Success();
-}
-
-// name type perm [ uid gid context ]
-Result<Success> Service::ParseSocket(std::vector<std::string>&& args) {
- if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
- !StartsWith(args[2], "seqpacket")) {
- return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
- }
- return AddDescriptor<SocketInfo>(std::move(args));
-}
-
-// name type perm [ uid gid context ]
-Result<Success> Service::ParseFile(std::vector<std::string>&& args) {
- if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
- return Error() << "file type must be 'r', 'w' or 'rw'";
- }
- if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
- return Error() << "file name must not be relative";
- }
- return AddDescriptor<FileInfo>(std::move(args));
-}
-
-Result<Success> Service::ParseUser(std::vector<std::string>&& args) {
- auto uid = DecodeUid(args[1]);
- if (!uid) {
- return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
- }
- uid_ = *uid;
- return Success();
-}
-
-Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) {
- args.erase(args.begin());
- writepid_files_ = std::move(args);
- return Success();
-}
-
-class Service::OptionParserMap : public KeywordMap<OptionParser> {
- public:
- OptionParserMap() {}
-
- private:
- const Map& map() const override;
-};
-
-const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
- constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
- // clang-format off
- static const Map option_parsers = {
- {"capabilities",
- {1, kMax, &Service::ParseCapabilities}},
- {"class", {1, kMax, &Service::ParseClass}},
- {"console", {0, 1, &Service::ParseConsole}},
- {"critical", {0, 0, &Service::ParseCritical}},
- {"disabled", {0, 0, &Service::ParseDisabled}},
- {"enter_namespace",
- {2, 2, &Service::ParseEnterNamespace}},
- {"file", {2, 2, &Service::ParseFile}},
- {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
- {"interface", {2, 2, &Service::ParseInterface}},
- {"ioprio", {2, 2, &Service::ParseIoprio}},
- {"keycodes", {1, kMax, &Service::ParseKeycodes}},
- {"memcg.limit_in_bytes",
- {1, 1, &Service::ParseMemcgLimitInBytes}},
- {"memcg.soft_limit_in_bytes",
- {1, 1, &Service::ParseMemcgSoftLimitInBytes}},
- {"memcg.swappiness",
- {1, 1, &Service::ParseMemcgSwappiness}},
- {"namespace", {1, 2, &Service::ParseNamespace}},
- {"oneshot", {0, 0, &Service::ParseOneshot}},
- {"onrestart", {1, kMax, &Service::ParseOnrestart}},
- {"oom_score_adjust",
- {1, 1, &Service::ParseOomScoreAdjust}},
- {"override", {0, 0, &Service::ParseOverride}},
- {"priority", {1, 1, &Service::ParsePriority}},
- {"restart_period",
- {1, 1, &Service::ParseRestartPeriod}},
- {"rlimit", {3, 3, &Service::ParseProcessRlimit}},
- {"seclabel", {1, 1, &Service::ParseSeclabel}},
- {"setenv", {2, 2, &Service::ParseSetenv}},
- {"shutdown", {1, 1, &Service::ParseShutdown}},
- {"sigstop", {0, 0, &Service::ParseSigstop}},
- {"socket", {3, 6, &Service::ParseSocket}},
- {"timeout_period",
- {1, 1, &Service::ParseTimeoutPeriod}},
- {"user", {1, 1, &Service::ParseUser}},
- {"writepid", {1, kMax, &Service::ParseWritepid}},
- };
- // clang-format on
- return option_parsers;
-}
-
-Result<Success> Service::ParseLine(std::vector<std::string>&& args) {
- static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args);
-
- if (!parser) return parser.error();
-
- return std::invoke(*parser, this, std::move(args));
-}
-
-Result<Success> Service::ExecStart() {
flags_ |= SVC_ONESHOT;
if (auto result = Start(); !result) {
@@ -830,14 +331,21 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
- LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
- << gid_ << "+" << supp_gids_.size() << " context "
+ LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
+ << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
<< (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
- return Success();
+ return {};
}
-Result<Success> Service::Start() {
+Result<void> Service::Start() {
+ if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+ ServiceList::GetInstance().DelayService(*this);
+ return Error() << "Cannot start an updatable service '" << name_
+ << "' before configs from APEXes are all loaded. "
+ << "Queued for execution.";
+ }
+
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
@@ -853,21 +361,21 @@
flags_ |= SVC_RESTART;
}
// It is not an error to try to start a service that is already running.
- return Success();
+ return {};
}
bool needs_console = (flags_ & SVC_CONSOLE);
if (needs_console) {
- if (console_.empty()) {
- console_ = default_console;
+ if (proc_attr_.console.empty()) {
+ proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
}
// Make sure that open call succeeds to ensure a console driver is
// properly registered for the device node
- int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+ int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
flags_ |= SVC_DISABLED;
- return ErrnoError() << "Couldn't open console '" << console_ << "'";
+ return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
}
close(console_fd);
}
@@ -889,11 +397,21 @@
scon = *result;
}
+ if (!IsRuntimeApexReady() && !pre_apexd_) {
+ // If this service is started before the runtime APEX gets available,
+ // mark it as pre-apexd one. Note that this marking is permanent. So
+ // for example, if the service is re-launched (e.g., due to crash),
+ // it is still recognized as pre-apexd... for consistency.
+ pre_apexd_ = true;
+ }
+
+ post_data_ = ServiceList::GetInstance().IsPostData();
+
LOG(INFO) << "starting service '" << name_ << "'...";
pid_t pid = -1;
- if (namespace_flags_) {
- pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
+ if (namespaces_.flags) {
+ pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
} else {
pid = fork();
}
@@ -901,79 +419,34 @@
if (pid == 0) {
umask(077);
- if (auto result = EnterNamespaces(); !result) {
- LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
- }
-
- if (namespace_flags_ & CLONE_NEWNS) {
- if (auto result = SetUpMountNamespace(); !result) {
- LOG(FATAL) << "Service '" << name_
- << "' could not set up mount namespace: " << result.error();
- }
- }
-
- if (namespace_flags_ & CLONE_NEWPID) {
- // This will fork again to run an init process inside the PID
- // namespace.
- if (auto result = SetUpPidNamespace(); !result) {
- LOG(FATAL) << "Service '" << name_
- << "' could not set up PID namespace: " << result.error();
- }
+ if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' failed to set up namespaces: " << result.error();
}
for (const auto& [key, value] : environment_vars_) {
setenv(key.c_str(), value.c_str(), 1);
}
- std::for_each(descriptors_.begin(), descriptors_.end(),
- std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
-
- // See if there were "writepid" instructions to write to files under /dev/cpuset/.
- auto cpuset_predicate = [](const std::string& path) {
- return StartsWith(path, "/dev/cpuset/");
- };
- auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
- if (iter == writepid_files_.end()) {
- // There were no "writepid" instructions for cpusets, check if the system default
- // cpuset is specified to be used for the process.
- std::string default_cpuset = GetProperty("ro.cpuset.default", "");
- if (!default_cpuset.empty()) {
- // Make sure the cpuset name starts and ends with '/'.
- // A single '/' means the 'root' cpuset.
- if (default_cpuset.front() != '/') {
- default_cpuset.insert(0, 1, '/');
- }
- if (default_cpuset.back() != '/') {
- default_cpuset.push_back('/');
- }
- writepid_files_.push_back(
- StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
- }
- }
- std::string pid_str = std::to_string(getpid());
- for (const auto& file : writepid_files_) {
- if (!WriteStringToFile(pid_str, file)) {
- PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
+ for (const auto& socket : sockets_) {
+ if (auto result = socket.CreateAndPublish(scon); !result) {
+ LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
}
}
- if (ioprio_class_ != IoSchedClass_NONE) {
- if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
- PLOG(ERROR) << "failed to set pid " << getpid()
- << " ioprio=" << ioprio_class_ << "," << ioprio_pri_;
+ for (const auto& file : files_) {
+ if (auto result = file.CreateAndPublish(); !result) {
+ LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
}
}
- if (needs_console) {
- setsid();
- OpenConsole();
- } else {
- ZapStdio();
+ if (auto result = WritePidToFiles(&writepid_files_); !result) {
+ LOG(ERROR) << "failed to write pid to files: " << result.error();
}
// As requested, set our gid, supplemental gids, uid, context, and
// priority. Aborts on failure.
- SetProcessAttributes();
+ SetProcessAttributesAndCaps();
if (!ExpandArgsAndExecv(args_, sigstop_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
@@ -991,7 +464,7 @@
std::string oom_str = std::to_string(oom_score_adjust_);
std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
if (!WriteStringToFile(oom_str, oom_file)) {
- PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+ PLOG(ERROR) << "couldn't write oom_score_adj";
}
}
@@ -1001,55 +474,97 @@
start_order_ = next_start_order_++;
process_cgroup_empty_ = false;
- errno = -createProcessGroup(uid_, pid_);
+ bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+ limit_percent_ != -1 || !limit_property_.empty();
+ errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
if (errno != 0) {
- PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
- << name_ << "'";
- } else {
+ PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+ << ") failed for service '" << name_ << "'";
+ } else if (use_memcg) {
if (swappiness_ != -1) {
- if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
+ if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
PLOG(ERROR) << "setProcessGroupSwappiness failed";
}
}
if (soft_limit_in_bytes_ != -1) {
- if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
+ if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
PLOG(ERROR) << "setProcessGroupSoftLimit failed";
}
}
- if (limit_in_bytes_ != -1) {
- if (!setProcessGroupLimit(uid_, pid_, limit_in_bytes_)) {
+ size_t computed_limit_in_bytes = limit_in_bytes_;
+ if (limit_percent_ != -1) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ long num_pages = sysconf(_SC_PHYS_PAGES);
+ if (page_size > 0 && num_pages > 0) {
+ size_t max_mem = SIZE_MAX;
+ if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+ max_mem = size_t(num_pages) * size_t(page_size);
+ }
+ computed_limit_in_bytes =
+ std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+ }
+ }
+
+ if (!limit_property_.empty()) {
+ // This ends up overwriting computed_limit_in_bytes but only if the
+ // property is defined.
+ computed_limit_in_bytes = android::base::GetUintProperty(
+ limit_property_, computed_limit_in_bytes, SIZE_MAX);
+ }
+
+ if (computed_limit_in_bytes != size_t(-1)) {
+ if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
PLOG(ERROR) << "setProcessGroupLimit failed";
}
}
}
NotifyStateChange("running");
- return Success();
+ return {};
}
-Result<Success> Service::StartIfNotDisabled() {
+Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
- return Success();
+ return {};
}
-Result<Success> Service::Enable() {
+Result<void> Service::Enable() {
flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
if (flags_ & SVC_DISABLED_START) {
return Start();
}
- return Success();
+ return {};
}
void Service::Reset() {
StopOrReset(SVC_RESET);
}
+void Service::ResetIfPostData() {
+ if (post_data_) {
+ if (flags_ & SVC_RUNNING) {
+ running_at_post_data_reset_ = true;
+ }
+ StopOrReset(SVC_RESET);
+ }
+}
+
+Result<void> Service::StartIfPostData() {
+ // Start the service, but only if it was started after /data was mounted,
+ // and it was still running when we reset the post-data services.
+ if (running_at_post_data_reset_) {
+ return Start();
+ }
+
+ return {};
+}
+
void Service::Stop() {
StopOrReset(SVC_DISABLED);
}
@@ -1120,37 +635,8 @@
}
}
-void Service::ZapStdio() const {
- int fd;
- fd = open("/dev/null", O_RDWR);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
-void Service::OpenConsole() const {
- int fd = open(console_.c_str(), O_RDWR);
- if (fd == -1) fd = open("/dev/null", O_RDWR);
- ioctl(fd, TIOCSCTTY, 0);
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- close(fd);
-}
-
-ServiceList::ServiceList() {}
-
-ServiceList& ServiceList::GetInstance() {
- static ServiceList instance;
- return instance;
-}
-
-void ServiceList::AddService(std::unique_ptr<Service> service) {
- services_.emplace_back(std::move(service));
-}
-
-std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
+Result<std::unique_ptr<Service>> Service::MakeTemporaryOneshotService(
+ const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
std::size_t command_arg = 1;
@@ -1161,13 +647,11 @@
}
}
if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
- LOG(ERROR) << "exec called with too many supplementary group ids";
- return nullptr;
+ return Error() << "exec called with too many supplementary group ids";
}
if (command_arg >= args.size()) {
- LOG(ERROR) << "exec called without command";
- return nullptr;
+ return Error() << "exec called without command";
}
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
@@ -1176,7 +660,6 @@
std::string name = "exec " + std::to_string(exec_count) + " (" + Join(str_args, " ") + ")";
unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;
- CapSet no_capabilities;
unsigned namespace_flags = 0;
std::string seclabel = "";
@@ -1187,8 +670,7 @@
if (command_arg > 3) {
uid = DecodeUid(args[2]);
if (!uid) {
- LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
- return nullptr;
+ return Error() << "Unable to decode UID for '" << args[2] << "': " << uid.error();
}
}
Result<gid_t> gid = 0;
@@ -1196,116 +678,21 @@
if (command_arg > 4) {
gid = DecodeUid(args[3]);
if (!gid) {
- LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
- return nullptr;
+ return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
}
std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
for (size_t i = 0; i < nr_supp_gids; ++i) {
auto supp_gid = DecodeUid(args[4 + i]);
if (!supp_gid) {
- LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
- << "': " << supp_gid.error();
- return nullptr;
+ return Error() << "Unable to decode GID for '" << args[4 + i]
+ << "': " << supp_gid.error();
}
supp_gids.push_back(*supp_gid);
}
}
- return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
- namespace_flags, seclabel, nullptr, str_args);
-}
-
-// Shutdown services in the opposite order that they were started.
-const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
- std::vector<Service*> shutdown_services;
- for (const auto& service : services_) {
- if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
- }
- std::sort(shutdown_services.begin(), shutdown_services.end(),
- [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
- return shutdown_services;
-}
-
-void ServiceList::RemoveService(const Service& svc) {
- auto svc_it = std::find_if(services_.begin(), services_.end(),
- [&svc] (const std::unique_ptr<Service>& s) {
- return svc.name() == s->name();
- });
- if (svc_it == services_.end()) {
- return;
- }
-
- services_.erase(svc_it);
-}
-
-void ServiceList::DumpState() const {
- for (const auto& s : services_) {
- s->DumpState();
- }
-}
-
-Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
- if (args.size() < 3) {
- return Error() << "services must have a name and a program";
- }
-
- const std::string& name = args[1];
- if (!IsValidName(name)) {
- return Error() << "invalid service name '" << name << "'";
- }
-
- Subcontext* restart_action_subcontext = nullptr;
- if (subcontexts_) {
- for (auto& subcontext : *subcontexts_) {
- if (StartsWith(filename, subcontext.path_prefix())) {
- restart_action_subcontext = &subcontext;
- break;
- }
- }
- }
-
- std::vector<std::string> str_args(args.begin() + 2, args.end());
-
- if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
- if (str_args[0] == "/sbin/watchdogd") {
- str_args[0] = "/system/bin/watchdogd";
- }
- }
-
- service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
- return Success();
-}
-
-Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- return service_ ? service_->ParseLine(std::move(args)) : Success();
-}
-
-Result<Success> ServiceParser::EndSection() {
- if (service_) {
- Service* old_service = service_list_->FindService(service_->name());
- if (old_service) {
- if (!service_->is_override()) {
- return Error() << "ignored duplicate definition of service '" << service_->name()
- << "'";
- }
-
- service_list_->RemoveService(*old_service);
- old_service = nullptr;
- }
-
- service_list_->AddService(std::move(service_));
- }
-
- return Success();
-}
-
-bool ServiceParser::IsValidName(const std::string& name) const {
- // Property names can be any length, but may only contain certain characters.
- // Property values can contain any characters, but may only be a certain length.
- // (The latter restriction is needed because `start` and `stop` work by writing
- // the service name to the "ctl.start" and "ctl.stop" properties.)
- return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+ return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
+ nullptr, str_args);
}
} // namespace init
diff --git a/init/service.h b/init/service.h
index c7beee9..6f79faa 100644
--- a/init/service.h
+++ b/init/service.h
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-#ifndef _INIT_SERVICE_H
-#define _INIT_SERVICE_H
+#pragma once
#include <signal.h>
-#include <sys/resource.h>
#include <sys/types.h>
#include <chrono>
@@ -33,9 +31,9 @@
#include "action.h"
#include "capabilities.h"
-#include "descriptors.h"
#include "keyword_map.h"
#include "parser.h"
+#include "service_utils.h"
#include "subcontext.h"
#define SVC_DISABLED 0x001 // do not autostart with class
@@ -63,24 +61,27 @@
namespace init {
class Service {
+ friend class ServiceParser;
+
public:
Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::vector<std::string>& args);
Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
- const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
- unsigned namespace_flags, const std::string& seclabel,
+ const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
- static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
+ static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
+ const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
- Result<Success> ParseLine(std::vector<std::string>&& args);
- Result<Success> ExecStart();
- Result<Success> Start();
- Result<Success> StartIfNotDisabled();
- Result<Success> Enable();
+ Result<void> ExecStart();
+ Result<void> Start();
+ Result<void> StartIfNotDisabled();
+ Result<void> StartIfPostData();
+ Result<void> Enable();
void Reset();
+ void ResetIfPostData();
void Stop();
void Terminate();
void Timeout();
@@ -105,16 +106,16 @@
pid_t pid() const { return pid_; }
android::base::boot_clock::time_point time_started() const { return time_started_; }
int crash_count() const { return crash_count_; }
- uid_t uid() const { return uid_; }
- gid_t gid() const { return gid_; }
- unsigned namespace_flags() const { return namespace_flags_; }
- const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+ uid_t uid() const { return proc_attr_.uid; }
+ gid_t gid() const { return proc_attr_.gid; }
+ int namespace_flags() const { return namespaces_.flags; }
+ const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }
const std::string& seclabel() const { return seclabel_; }
const std::vector<int>& keycodes() const { return keycodes_; }
- IoSchedClass ioprio_class() const { return ioprio_class_; }
- int ioprio_pri() const { return ioprio_pri_; }
+ IoSchedClass ioprio_class() const { return proc_attr_.ioprio_class; }
+ int ioprio_pri() const { return proc_attr_.ioprio_pri; }
const std::set<std::string>& interfaces() const { return interfaces_; }
- int priority() const { return priority_; }
+ int priority() const { return proc_attr_.priority; }
int oom_score_adjust() const { return oom_score_adjust_; }
bool is_override() const { return override_; }
bool process_cgroup_empty() const { return process_cgroup_empty_; }
@@ -123,61 +124,20 @@
std::chrono::seconds restart_period() const { return restart_period_; }
std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
const std::vector<std::string>& args() const { return args_; }
+ bool is_updatable() const { return updatable_; }
+ bool is_post_data() const { return post_data_; }
private:
- using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
- class OptionParserMap;
-
- Result<Success> SetUpMountNamespace() const;
- Result<Success> SetUpPidNamespace() const;
- Result<Success> EnterNamespaces() const;
void NotifyStateChange(const std::string& new_state) const;
void StopOrReset(int how);
- void ZapStdio() const;
- void OpenConsole() const;
void KillProcessGroup(int signal);
- void SetProcessAttributes();
-
- Result<Success> ParseCapabilities(std::vector<std::string>&& args);
- Result<Success> ParseClass(std::vector<std::string>&& args);
- Result<Success> ParseConsole(std::vector<std::string>&& args);
- Result<Success> ParseCritical(std::vector<std::string>&& args);
- Result<Success> ParseDisabled(std::vector<std::string>&& args);
- Result<Success> ParseEnterNamespace(std::vector<std::string>&& args);
- Result<Success> ParseGroup(std::vector<std::string>&& args);
- Result<Success> ParsePriority(std::vector<std::string>&& args);
- Result<Success> ParseInterface(std::vector<std::string>&& args);
- Result<Success> ParseIoprio(std::vector<std::string>&& args);
- Result<Success> ParseKeycodes(std::vector<std::string>&& args);
- Result<Success> ParseOneshot(std::vector<std::string>&& args);
- Result<Success> ParseOnrestart(std::vector<std::string>&& args);
- Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
- Result<Success> ParseOverride(std::vector<std::string>&& args);
- Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
- Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
- Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
- Result<Success> ParseNamespace(std::vector<std::string>&& args);
- Result<Success> ParseProcessRlimit(std::vector<std::string>&& args);
- Result<Success> ParseRestartPeriod(std::vector<std::string>&& args);
- Result<Success> ParseSeclabel(std::vector<std::string>&& args);
- Result<Success> ParseSetenv(std::vector<std::string>&& args);
- Result<Success> ParseShutdown(std::vector<std::string>&& args);
- Result<Success> ParseSigstop(std::vector<std::string>&& args);
- Result<Success> ParseSocket(std::vector<std::string>&& args);
- Result<Success> ParseTimeoutPeriod(std::vector<std::string>&& args);
- Result<Success> ParseFile(std::vector<std::string>&& args);
- Result<Success> ParseUser(std::vector<std::string>&& args);
- Result<Success> ParseWritepid(std::vector<std::string>&& args);
-
- template <typename T>
- Result<Success> AddDescriptor(std::vector<std::string>&& args);
+ void SetProcessAttributesAndCaps();
static unsigned long next_start_order_;
static bool is_exec_service_running_;
std::string name_;
std::set<std::string> classnames_;
- std::string console_;
unsigned flags_;
pid_t pid_;
@@ -185,17 +145,14 @@
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
- uid_t uid_;
- gid_t gid_;
- std::vector<gid_t> supp_gids_;
- CapSet capabilities_;
- unsigned namespace_flags_;
- // Pair of namespace type, path to namespace.
- std::vector<std::pair<int, std::string>> namespaces_to_enter_;
+ std::optional<CapSet> capabilities_;
+ ProcessAttributes proc_attr_;
+ NamespaceInfo namespaces_;
std::string seclabel_;
- std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
+ std::vector<SocketDescriptor> sockets_;
+ std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.
@@ -207,15 +164,14 @@
// keycodes for triggering this service via /dev/input/input*
std::vector<int> keycodes_;
- IoSchedClass ioprio_class_;
- int ioprio_pri_;
- int priority_;
-
int oom_score_adjust_;
- int swappiness_;
- int soft_limit_in_bytes_;
- int limit_in_bytes_;
+ int swappiness_ = -1;
+ int soft_limit_in_bytes_ = -1;
+
+ int limit_in_bytes_ = -1;
+ int limit_percent_ = -1;
+ std::string limit_property_;
bool process_cgroup_empty_ = false;
@@ -223,79 +179,23 @@
unsigned long start_order_;
- std::vector<std::pair<int, rlimit>> rlimits_;
-
bool sigstop_ = false;
std::chrono::seconds restart_period_ = 5s;
std::optional<std::chrono::seconds> timeout_period_;
+ bool updatable_ = false;
+
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
-};
-class ServiceList {
- public:
- static ServiceList& GetInstance();
+ bool pre_apexd_ = false;
- // Exposed for testing
- ServiceList();
+ bool post_data_ = false;
- void AddService(std::unique_ptr<Service> service);
- void RemoveService(const Service& svc);
-
- template <typename T, typename F = decltype(&Service::name)>
- Service* FindService(T value, F function = &Service::name) const {
- auto svc = std::find_if(services_.begin(), services_.end(),
- [&function, &value](const std::unique_ptr<Service>& s) {
- return std::invoke(function, s) == value;
- });
- if (svc != services_.end()) {
- return svc->get();
- }
- return nullptr;
- }
-
- Service* FindInterface(const std::string& interface_name) {
- for (const auto& svc : services_) {
- if (svc->interfaces().count(interface_name) > 0) {
- return svc.get();
- }
- }
-
- return nullptr;
- }
-
- void DumpState() const;
-
- auto begin() const { return services_.begin(); }
- auto end() const { return services_.end(); }
- const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
- const std::vector<Service*> services_in_shutdown_order() const;
-
- private:
- std::vector<std::unique_ptr<Service>> services_;
-};
-
-class ServiceParser : public SectionParser {
- public:
- ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
- : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
-
- private:
- bool IsValidName(const std::string& name) const;
-
- ServiceList* service_list_;
- std::vector<Subcontext>* subcontexts_;
- std::unique_ptr<Service> service_;
+ bool running_at_post_data_reset_ = false;
};
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/service_list.cpp b/init/service_list.cpp
new file mode 100644
index 0000000..3a48183
--- /dev/null
+++ b/init/service_list.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service_list.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace init {
+
+ServiceList::ServiceList() {}
+
+ServiceList& ServiceList::GetInstance() {
+ static ServiceList instance;
+ return instance;
+}
+
+void ServiceList::AddService(std::unique_ptr<Service> service) {
+ services_.emplace_back(std::move(service));
+}
+
+// Shutdown services in the opposite order that they were started.
+const std::vector<Service*> ServiceList::services_in_shutdown_order() const {
+ std::vector<Service*> shutdown_services;
+ for (const auto& service : services_) {
+ if (service->start_order() > 0) shutdown_services.emplace_back(service.get());
+ }
+ std::sort(shutdown_services.begin(), shutdown_services.end(),
+ [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });
+ return shutdown_services;
+}
+
+void ServiceList::RemoveService(const Service& svc) {
+ auto svc_it = std::find_if(
+ services_.begin(), services_.end(),
+ [&svc](const std::unique_ptr<Service>& s) { return svc.name() == s->name(); });
+ if (svc_it == services_.end()) {
+ return;
+ }
+
+ services_.erase(svc_it);
+}
+
+void ServiceList::DumpState() const {
+ for (const auto& s : services_) {
+ s->DumpState();
+ }
+}
+
+void ServiceList::MarkPostData() {
+ post_data_ = true;
+}
+
+bool ServiceList::IsPostData() {
+ return post_data_;
+}
+
+void ServiceList::MarkServicesUpdate() {
+ services_update_finished_ = true;
+
+ // start the delayed services
+ for (const auto& name : delayed_service_names_) {
+ Service* service = FindService(name);
+ if (service == nullptr) {
+ LOG(ERROR) << "delayed service '" << name << "' could not be found.";
+ continue;
+ }
+ if (auto result = service->Start(); !result) {
+ LOG(ERROR) << result.error().message();
+ }
+ }
+ delayed_service_names_.clear();
+}
+
+void ServiceList::DelayService(const Service& service) {
+ if (services_update_finished_) {
+ LOG(ERROR) << "Cannot delay the start of service '" << service.name()
+ << "' because all services are already updated. Ignoring.";
+ return;
+ }
+ delayed_service_names_.emplace_back(service.name());
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_list.h b/init/service_list.h
new file mode 100644
index 0000000..2136a21
--- /dev/null
+++ b/init/service_list.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "service.h"
+
+namespace android {
+namespace init {
+
+class ServiceList {
+ public:
+ static ServiceList& GetInstance();
+
+ // Exposed for testing
+ ServiceList();
+
+ void AddService(std::unique_ptr<Service> service);
+ void RemoveService(const Service& svc);
+
+ template <typename T, typename F = decltype(&Service::name)>
+ Service* FindService(T value, F function = &Service::name) const {
+ auto svc = std::find_if(services_.begin(), services_.end(),
+ [&function, &value](const std::unique_ptr<Service>& s) {
+ return std::invoke(function, s) == value;
+ });
+ if (svc != services_.end()) {
+ return svc->get();
+ }
+ return nullptr;
+ }
+
+ Service* FindInterface(const std::string& interface_name) {
+ for (const auto& svc : services_) {
+ if (svc->interfaces().count(interface_name) > 0) {
+ return svc.get();
+ }
+ }
+
+ return nullptr;
+ }
+
+ void DumpState() const;
+
+ auto begin() const { return services_.begin(); }
+ auto end() const { return services_.end(); }
+ const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
+ const std::vector<Service*> services_in_shutdown_order() const;
+
+ void MarkPostData();
+ bool IsPostData();
+ void MarkServicesUpdate();
+ bool IsServicesUpdated() const { return services_update_finished_; }
+ void DelayService(const Service& service);
+
+ private:
+ std::vector<std::unique_ptr<Service>> services_;
+
+ bool post_data_ = false;
+ bool services_update_finished_ = false;
+ std::vector<std::string> delayed_service_names_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
new file mode 100644
index 0000000..e45e804
--- /dev/null
+++ b/init/service_parser.cpp
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service_parser.h"
+
+#include <linux/input.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <hidl-util/FQName.h>
+#include <system/thread_defs.h>
+
+#include "rlimit_parser.h"
+#include "service_utils.h"
+#include "util.h"
+
+#if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
+using android::base::ParseInt;
+using android::base::Split;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<void> ServiceParser::ParseCapabilities(std::vector<std::string>&& args) {
+ service_->capabilities_ = 0;
+
+ if (!CapAmbientSupported()) {
+ return Error()
+ << "capabilities requested but the kernel does not support ambient capabilities";
+ }
+
+ unsigned int last_valid_cap = GetLastValidCap();
+ if (last_valid_cap >= service_->capabilities_->size()) {
+ LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
+ }
+
+ for (size_t i = 1; i < args.size(); i++) {
+ const std::string& arg = args[i];
+ int res = LookupCap(arg);
+ if (res < 0) {
+ return Errorf("invalid capability '{}'", arg);
+ }
+ unsigned int cap = static_cast<unsigned int>(res); // |res| is >= 0.
+ if (cap > last_valid_cap) {
+ return Errorf("capability '{}' not supported by the kernel", arg);
+ }
+ (*service_->capabilities_)[cap] = true;
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {
+ service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end());
+ return {};
+}
+
+Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_CONSOLE;
+ service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
+ return {};
+}
+
+Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_CRITICAL;
+ return {};
+}
+
+Result<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_DISABLED;
+ service_->flags_ |= SVC_RC_DISABLED;
+ return {};
+}
+
+Result<void> ServiceParser::ParseEnterNamespace(std::vector<std::string>&& args) {
+ if (args[1] != "net") {
+ return Error() << "Init only supports entering network namespaces";
+ }
+ if (!service_->namespaces_.namespaces_to_enter.empty()) {
+ return Error() << "Only one network namespace may be entered";
+ }
+ // Network namespaces require that /sys is remounted, otherwise the old adapters will still be
+ // present. Therefore, they also require mount namespaces.
+ service_->namespaces_.flags |= CLONE_NEWNS;
+ service_->namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2]));
+ return {};
+}
+
+Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {
+ auto gid = DecodeUid(args[1]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error();
+ }
+ service_->proc_attr_.gid = *gid;
+
+ for (std::size_t n = 2; n < args.size(); n++) {
+ gid = DecodeUid(args[n]);
+ if (!gid) {
+ return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error();
+ }
+ service_->proc_attr_.supp_gids.emplace_back(*gid);
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) {
+ service_->proc_attr_.priority = 0;
+ if (!ParseInt(args[1], &service_->proc_attr_.priority,
+ static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
+ static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
+ return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
+ ANDROID_PRIORITY_LOWEST);
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) {
+ const std::string& interface_name = args[1];
+ const std::string& instance_name = args[2];
+
+ FQName fq_name;
+ if (!FQName::parse(interface_name, &fq_name)) {
+ return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
+ }
+
+ if (!fq_name.isFullyQualified()) {
+ return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+ }
+
+ if (fq_name.isValidValueName()) {
+ return Error() << "Interface name must not be a value name '" << interface_name << "'";
+ }
+
+ const std::string fullname = interface_name + "/" + instance_name;
+
+ for (const auto& svc : *service_list_) {
+ if (svc->interfaces().count(fullname) > 0) {
+ return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
+ << " but is already defined by " << svc->name();
+ }
+ }
+
+ service_->interfaces_.insert(fullname);
+
+ return {};
+}
+
+Result<void> ServiceParser::ParseIoprio(std::vector<std::string>&& args) {
+ if (!ParseInt(args[2], &service_->proc_attr_.ioprio_pri, 0, 7)) {
+ return Error() << "priority value must be range 0 - 7";
+ }
+
+ if (args[1] == "rt") {
+ service_->proc_attr_.ioprio_class = IoSchedClass_RT;
+ } else if (args[1] == "be") {
+ service_->proc_attr_.ioprio_class = IoSchedClass_BE;
+ } else if (args[1] == "idle") {
+ service_->proc_attr_.ioprio_class = IoSchedClass_IDLE;
+ } else {
+ return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+ }
+
+ return {};
+}
+
+Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {
+ auto it = args.begin() + 1;
+ if (args.size() == 2 && StartsWith(args[1], "$")) {
+ auto expanded = ExpandProps(args[1]);
+ if (!expanded) {
+ return expanded.error();
+ }
+
+ // If the property is not set, it defaults to none, in which case there are no keycodes
+ // for this service.
+ if (expanded == "none") {
+ return {};
+ }
+
+ args = Split(*expanded, ",");
+ it = args.begin();
+ }
+
+ for (; it != args.end(); ++it) {
+ int code;
+ if (ParseInt(*it, &code, 0, KEY_MAX)) {
+ for (auto& key : service_->keycodes_) {
+ if (key == code) return Error() << "duplicate keycode: " << *it;
+ }
+ service_->keycodes_.insert(
+ std::upper_bound(service_->keycodes_.begin(), service_->keycodes_.end(), code),
+ code);
+ } else {
+ return Error() << "invalid keycode: " << *it;
+ }
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) {
+ service_->flags_ |= SVC_ONESHOT;
+ return {};
+}
+
+Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ int line = service_->onrestart_.NumCommands() + 1;
+ if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result) {
+ return Error() << "cannot add Onrestart command: " << result.error();
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseNamespace(std::vector<std::string>&& args) {
+ for (size_t i = 1; i < args.size(); i++) {
+ if (args[i] == "pid") {
+ service_->namespaces_.flags |= CLONE_NEWPID;
+ // PID namespaces require mount namespaces.
+ service_->namespaces_.flags |= CLONE_NEWNS;
+ } else if (args[i] == "mnt") {
+ service_->namespaces_.flags |= CLONE_NEWNS;
+ } else {
+ return Error() << "namespace must be 'pid' or 'mnt'";
+ }
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->oom_score_adjust_, -1000, 1000)) {
+ return Error() << "oom_score_adjust value must be in range -1000 - +1000";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseOverride(std::vector<std::string>&& args) {
+ service_->override_ = true;
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSwappiness(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->swappiness_, 0)) {
+ return Error() << "swappiness value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->limit_in_bytes_, 0)) {
+ return Error() << "limit_in_bytes value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->limit_percent_, 0)) {
+ return Error() << "limit_percent value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+ service_->limit_property_ = std::move(args[1]);
+ return {};
+}
+
+Result<void> ServiceParser::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {
+ if (!ParseInt(args[1], &service_->soft_limit_in_bytes_, 0)) {
+ return Error() << "soft_limit_in_bytes value must be equal or greater than 0";
+ }
+ return {};
+}
+
+Result<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) {
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
+
+ service_->proc_attr_.rlimits.emplace_back(*rlimit);
+ return {};
+}
+
+Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 5)) {
+ return Error() << "restart_period value must be an integer >= 5";
+ }
+ service_->restart_period_ = std::chrono::seconds(period);
+ return {};
+}
+
+Result<void> ServiceParser::ParseSeclabel(std::vector<std::string>&& args) {
+ service_->seclabel_ = std::move(args[1]);
+ return {};
+}
+
+Result<void> ServiceParser::ParseSigstop(std::vector<std::string>&& args) {
+ service_->sigstop_ = true;
+ return {};
+}
+
+Result<void> ServiceParser::ParseSetenv(std::vector<std::string>&& args) {
+ service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
+ return {};
+}
+
+Result<void> ServiceParser::ParseShutdown(std::vector<std::string>&& args) {
+ if (args[1] == "critical") {
+ service_->flags_ |= SVC_SHUTDOWN_CRITICAL;
+ return {};
+ }
+ return Error() << "Invalid shutdown option";
+}
+
+Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {
+ int period;
+ if (!ParseInt(args[1], &period, 1)) {
+ return Error() << "timeout_period value must be an integer >= 1";
+ }
+ service_->timeout_period_ = std::chrono::seconds(period);
+ return {};
+}
+
+// name type perm [ uid gid context ]
+Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
+ SocketDescriptor socket;
+ socket.name = std::move(args[1]);
+
+ auto types = Split(args[2], "+");
+ if (types[0] == "stream") {
+ socket.type = SOCK_STREAM;
+ } else if (types[0] == "dgram") {
+ socket.type = SOCK_DGRAM;
+ } else if (types[0] == "seqpacket") {
+ socket.type = SOCK_SEQPACKET;
+ } else {
+ return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0]
+ << "' instead.";
+ }
+
+ if (types.size() > 1) {
+ if (types.size() == 2 && types[1] == "passcred") {
+ socket.passcred = true;
+ } else {
+ return Error() << "Only 'passcred' may be used to modify the socket type";
+ }
+ }
+
+ errno = 0;
+ char* end = nullptr;
+ socket.perm = strtol(args[3].c_str(), &end, 8);
+ if (errno != 0) {
+ return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+ }
+ if (end == args[3].c_str() || *end != '\0') {
+ errno = EINVAL;
+ return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+ }
+
+ if (args.size() > 4) {
+ auto uid = DecodeUid(args[4]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
+ }
+ socket.uid = *uid;
+ }
+
+ if (args.size() > 5) {
+ auto gid = DecodeUid(args[5]);
+ if (!gid) {
+ return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
+ }
+ socket.gid = *gid;
+ }
+
+ socket.context = args.size() > 6 ? args[6] : "";
+
+ auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
+ [&socket](const auto& other) { return socket.name == other.name; });
+
+ if (old != service_->sockets_.end()) {
+ return Error() << "duplicate socket descriptor '" << socket.name << "'";
+ }
+
+ service_->sockets_.emplace_back(std::move(socket));
+
+ return {};
+}
+
+// name type
+Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
+ if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
+ return Error() << "file type must be 'r', 'w' or 'rw'";
+ }
+
+ FileDescriptor file;
+ file.type = args[2];
+
+ auto file_name = ExpandProps(args[1]);
+ if (!file_name) {
+ return Error() << "Could not expand file path ': " << file_name.error();
+ }
+ file.name = *file_name;
+ if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
+ return Error() << "file name must not be relative";
+ }
+
+ auto old = std::find_if(service_->files_.begin(), service_->files_.end(),
+ [&file](const auto& other) { return other.name == file.name; });
+
+ if (old != service_->files_.end()) {
+ return Error() << "duplicate file descriptor '" << file.name << "'";
+ }
+
+ service_->files_.emplace_back(std::move(file));
+
+ return {};
+}
+
+Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
+ auto uid = DecodeUid(args[1]);
+ if (!uid) {
+ return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
+ }
+ service_->proc_attr_.uid = *uid;
+ return {};
+}
+
+Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
+ args.erase(args.begin());
+ service_->writepid_files_ = std::move(args);
+ return {};
+}
+
+Result<void> ServiceParser::ParseUpdatable(std::vector<std::string>&& args) {
+ service_->updatable_ = true;
+ return {};
+}
+
+const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {
+ constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ // clang-format off
+ static const KeywordMap<ServiceParser::OptionParser> parser_map = {
+ {"capabilities",
+ {0, kMax, &ServiceParser::ParseCapabilities}},
+ {"class", {1, kMax, &ServiceParser::ParseClass}},
+ {"console", {0, 1, &ServiceParser::ParseConsole}},
+ {"critical", {0, 0, &ServiceParser::ParseCritical}},
+ {"disabled", {0, 0, &ServiceParser::ParseDisabled}},
+ {"enter_namespace",
+ {2, 2, &ServiceParser::ParseEnterNamespace}},
+ {"file", {2, 2, &ServiceParser::ParseFile}},
+ {"group", {1, NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
+ {"interface", {2, 2, &ServiceParser::ParseInterface}},
+ {"ioprio", {2, 2, &ServiceParser::ParseIoprio}},
+ {"keycodes", {1, kMax, &ServiceParser::ParseKeycodes}},
+ {"memcg.limit_in_bytes",
+ {1, 1, &ServiceParser::ParseMemcgLimitInBytes}},
+ {"memcg.limit_percent",
+ {1, 1, &ServiceParser::ParseMemcgLimitPercent}},
+ {"memcg.limit_property",
+ {1, 1, &ServiceParser::ParseMemcgLimitProperty}},
+ {"memcg.soft_limit_in_bytes",
+ {1, 1, &ServiceParser::ParseMemcgSoftLimitInBytes}},
+ {"memcg.swappiness",
+ {1, 1, &ServiceParser::ParseMemcgSwappiness}},
+ {"namespace", {1, 2, &ServiceParser::ParseNamespace}},
+ {"oneshot", {0, 0, &ServiceParser::ParseOneshot}},
+ {"onrestart", {1, kMax, &ServiceParser::ParseOnrestart}},
+ {"oom_score_adjust",
+ {1, 1, &ServiceParser::ParseOomScoreAdjust}},
+ {"override", {0, 0, &ServiceParser::ParseOverride}},
+ {"priority", {1, 1, &ServiceParser::ParsePriority}},
+ {"restart_period",
+ {1, 1, &ServiceParser::ParseRestartPeriod}},
+ {"rlimit", {3, 3, &ServiceParser::ParseProcessRlimit}},
+ {"seclabel", {1, 1, &ServiceParser::ParseSeclabel}},
+ {"setenv", {2, 2, &ServiceParser::ParseSetenv}},
+ {"shutdown", {1, 1, &ServiceParser::ParseShutdown}},
+ {"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
+ {"socket", {3, 6, &ServiceParser::ParseSocket}},
+ {"timeout_period",
+ {1, 1, &ServiceParser::ParseTimeoutPeriod}},
+ {"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
+ {"user", {1, 1, &ServiceParser::ParseUser}},
+ {"writepid", {1, kMax, &ServiceParser::ParseWritepid}},
+ };
+ // clang-format on
+ return parser_map;
+}
+
+Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() < 3) {
+ return Error() << "services must have a name and a program";
+ }
+
+ const std::string& name = args[1];
+ if (!IsValidName(name)) {
+ return Error() << "invalid service name '" << name << "'";
+ }
+
+ filename_ = filename;
+
+ Subcontext* restart_action_subcontext = nullptr;
+ if (subcontexts_) {
+ for (auto& subcontext : *subcontexts_) {
+ if (StartsWith(filename, subcontext.path_prefix())) {
+ restart_action_subcontext = &subcontext;
+ break;
+ }
+ }
+ }
+
+ std::vector<std::string> str_args(args.begin() + 2, args.end());
+
+ if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {
+ if (str_args[0] == "/sbin/watchdogd") {
+ str_args[0] = "/system/bin/watchdogd";
+ }
+ }
+
+ service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
+ return {};
+}
+
+Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ if (!service_) {
+ return {};
+ }
+
+ auto parser = GetParserMap().Find(args);
+
+ if (!parser) return parser.error();
+
+ return std::invoke(*parser, this, std::move(args));
+}
+
+Result<void> ServiceParser::EndSection() {
+ if (!service_) {
+ return {};
+ }
+
+ if (interface_inheritance_hierarchy_) {
+ std::set<std::string> interface_names;
+ for (const std::string& intf : service_->interfaces()) {
+ interface_names.insert(Split(intf, "/")[0]);
+ }
+ std::ostringstream error_stream;
+ for (const std::string& intf : interface_names) {
+ if (interface_inheritance_hierarchy_->count(intf) == 0) {
+ error_stream << "\nInterface is not in the known set of hidl_interfaces: '" << intf
+ << "'. Please ensure the interface is spelled correctly and built "
+ << "by a hidl_interface target.";
+ continue;
+ }
+ const std::set<std::string>& required_interfaces =
+ (*interface_inheritance_hierarchy_)[intf];
+ std::set<std::string> diff;
+ std::set_difference(required_interfaces.begin(), required_interfaces.end(),
+ interface_names.begin(), interface_names.end(),
+ std::inserter(diff, diff.begin()));
+ if (!diff.empty()) {
+ error_stream << "\nInterface '" << intf << "' requires its full inheritance "
+ << "hierarchy to be listed in this init_rc file. Missing "
+ << "interfaces: [" << base::Join(diff, " ") << "]";
+ }
+ }
+ const std::string& errors = error_stream.str();
+ if (!errors.empty()) {
+ return Error() << errors;
+ }
+ }
+
+ Service* old_service = service_list_->FindService(service_->name());
+ if (old_service) {
+ if (!service_->is_override()) {
+ return Error() << "ignored duplicate definition of service '" << service_->name()
+ << "'";
+ }
+
+ if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) {
+ return Error() << "cannot update a non-updatable service '" << service_->name()
+ << "' with a config in APEX";
+ }
+
+ service_list_->RemoveService(*old_service);
+ old_service = nullptr;
+ }
+
+ service_list_->AddService(std::move(service_));
+
+ return {};
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+ // Property names can be any length, but may only contain certain characters.
+ // Property values can contain any characters, but may only be a certain length.
+ // (The latter restriction is needed because `start` and `stop` work by writing
+ // the service name to the "ctl.start" and "ctl.stop" properties.)
+ return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_parser.h b/init/service_parser.h
new file mode 100644
index 0000000..98ab15a
--- /dev/null
+++ b/init/service_parser.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "parser.h"
+#include "service.h"
+#include "service_list.h"
+#include "subcontext.h"
+
+namespace android {
+namespace init {
+
+using InterfaceInheritanceHierarchyMap = std::map<std::string, std::set<std::string>>;
+
+class ServiceParser : public SectionParser {
+ public:
+ ServiceParser(
+ ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+ const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
+ : service_list_(service_list),
+ subcontexts_(subcontexts),
+ interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
+ service_(nullptr) {}
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
+ void EndFile() override { filename_ = ""; }
+
+ private:
+ using OptionParser = Result<void> (ServiceParser::*)(std::vector<std::string>&& args);
+ const KeywordMap<ServiceParser::OptionParser>& GetParserMap() const;
+
+ Result<void> ParseCapabilities(std::vector<std::string>&& args);
+ Result<void> ParseClass(std::vector<std::string>&& args);
+ Result<void> ParseConsole(std::vector<std::string>&& args);
+ Result<void> ParseCritical(std::vector<std::string>&& args);
+ Result<void> ParseDisabled(std::vector<std::string>&& args);
+ Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
+ Result<void> ParseGroup(std::vector<std::string>&& args);
+ Result<void> ParsePriority(std::vector<std::string>&& args);
+ Result<void> ParseInterface(std::vector<std::string>&& args);
+ Result<void> ParseIoprio(std::vector<std::string>&& args);
+ Result<void> ParseKeycodes(std::vector<std::string>&& args);
+ Result<void> ParseOneshot(std::vector<std::string>&& args);
+ Result<void> ParseOnrestart(std::vector<std::string>&& args);
+ Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);
+ Result<void> ParseOverride(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+ Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);
+ Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+ Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);
+ Result<void> ParseNamespace(std::vector<std::string>&& args);
+ Result<void> ParseProcessRlimit(std::vector<std::string>&& args);
+ Result<void> ParseRestartPeriod(std::vector<std::string>&& args);
+ Result<void> ParseSeclabel(std::vector<std::string>&& args);
+ Result<void> ParseSetenv(std::vector<std::string>&& args);
+ Result<void> ParseShutdown(std::vector<std::string>&& args);
+ Result<void> ParseSigstop(std::vector<std::string>&& args);
+ Result<void> ParseSocket(std::vector<std::string>&& args);
+ Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
+ Result<void> ParseFile(std::vector<std::string>&& args);
+ Result<void> ParseUser(std::vector<std::string>&& args);
+ Result<void> ParseWritepid(std::vector<std::string>&& args);
+ Result<void> ParseUpdatable(std::vector<std::string>&& args);
+
+ bool IsValidName(const std::string& name) const;
+
+ ServiceList* service_list_;
+ std::vector<Subcontext>* subcontexts_;
+ std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
+ std::unique_ptr<Service> service_;
+ std::string filename_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 194aa2b..c9cc7bd 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -30,7 +30,7 @@
TEST(service, pod_initialized) {
constexpr auto memory_size = sizeof(Service);
- alignas(alignof(Service)) char old_memory[memory_size];
+ alignas(alignof(Service)) unsigned char old_memory[memory_size];
for (std::size_t i = 0; i < memory_size; ++i) {
old_memory[i] = 0xFF;
@@ -45,7 +45,7 @@
EXPECT_EQ(0, service_in_old_memory->crash_count());
EXPECT_EQ(0U, service_in_old_memory->uid());
EXPECT_EQ(0U, service_in_old_memory->gid());
- EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
+ EXPECT_EQ(0, service_in_old_memory->namespace_flags());
EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory->priority());
@@ -57,14 +57,14 @@
}
Service* service_in_old_memory2 = new (old_memory) Service(
- "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
+ "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
EXPECT_EQ(0U, service_in_old_memory2->flags());
EXPECT_EQ(0, service_in_old_memory2->pid());
EXPECT_EQ(0, service_in_old_memory2->crash_count());
EXPECT_EQ(0U, service_in_old_memory2->uid());
EXPECT_EQ(0U, service_in_old_memory2->gid());
- EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
+ EXPECT_EQ(0, service_in_old_memory2->namespace_flags());
EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
EXPECT_EQ(0, service_in_old_memory2->priority());
@@ -75,15 +75,15 @@
TEST(service, make_temporary_oneshot_service_invalid_syntax) {
std::vector<std::string> args;
// Nothing.
- ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+ ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
// No arguments to 'exec'.
args.push_back("exec");
- ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+ ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
// No command in "exec --".
args.push_back("--");
- ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+ ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
}
TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
@@ -97,7 +97,7 @@
}
args.push_back("--");
args.push_back("/system/bin/id");
- ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+ ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
}
static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
@@ -122,8 +122,9 @@
}
args.push_back("/system/bin/toybox");
args.push_back("id");
- auto svc = Service::MakeTemporaryOneshotService(args);
- ASSERT_NE(nullptr, svc);
+ auto service_ret = Service::MakeTemporaryOneshotService(args);
+ ASSERT_TRUE(service_ret);
+ auto svc = std::move(*service_ret);
if (seclabel) {
ASSERT_EQ("u:r:su:s0", svc->seclabel());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
new file mode 100644
index 0000000..836145d
--- /dev/null
+++ b/init/service_utils.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service_utils.h"
+
+#include <grp.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sockets.h>
+#include <processgroup/processgroup.h>
+
+#include "mount_namespace.h"
+#include "util.h"
+
+using android::base::GetProperty;
+using android::base::StartsWith;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
+namespace android {
+namespace init {
+
+namespace {
+
+Result<void> EnterNamespace(int nstype, const char* path) {
+ auto fd = unique_fd{open(path, O_RDONLY | O_CLOEXEC)};
+ if (fd == -1) {
+ return ErrnoError() << "Could not open namespace at " << path;
+ }
+ if (setns(fd, nstype) == -1) {
+ return ErrnoError() << "Could not setns() namespace at " << path;
+ }
+ return {};
+}
+
+Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
+ constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
+
+ // Recursively remount / as slave like zygote does so unmounting and mounting /proc
+ // doesn't interfere with the parent namespace's /proc mount. This will also
+ // prevent any other mounts/unmounts initiated by the service from interfering
+ // with the parent namespace but will still allow mount events from the parent
+ // namespace to propagate to the child.
+ if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
+ return ErrnoError() << "Could not remount(/) recursively as slave";
+ }
+
+ // umount() then mount() /proc and/or /sys
+ // Note that it is not sufficient to mount with MS_REMOUNT.
+ if (remount_proc) {
+ if (umount("/proc") == -1) {
+ return ErrnoError() << "Could not umount(/proc)";
+ }
+ if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/proc)";
+ }
+ }
+ if (remount_sys) {
+ if (umount2("/sys", MNT_DETACH) == -1) {
+ return ErrnoError() << "Could not umount(/sys)";
+ }
+ if (mount("", "/sys", "sysfs", kSafeFlags, "") == -1) {
+ return ErrnoError() << "Could not mount(/sys)";
+ }
+ }
+ return {};
+}
+
+Result<void> SetUpPidNamespace(const char* name) {
+ if (prctl(PR_SET_NAME, name) == -1) {
+ return ErrnoError() << "Could not set name";
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ return ErrnoError() << "Could not fork init inside the PID namespace";
+ }
+
+ if (child_pid > 0) {
+ // So that we exit with the right status.
+ static int init_exitstatus = 0;
+ signal(SIGTERM, [](int) { _exit(init_exitstatus); });
+
+ pid_t waited_pid;
+ int status;
+ while ((waited_pid = wait(&status)) > 0) {
+ // This loop will end when there are no processes left inside the
+ // PID namespace or when the init process inside the PID namespace
+ // gets a signal.
+ if (waited_pid == child_pid) {
+ init_exitstatus = status;
+ }
+ }
+ if (!WIFEXITED(init_exitstatus)) {
+ _exit(EXIT_FAILURE);
+ }
+ _exit(WEXITSTATUS(init_exitstatus));
+ }
+ return {};
+}
+
+void ZapStdio() {
+ auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+}
+
+void OpenConsole(const std::string& console) {
+ auto fd = unique_fd{open(console.c_str(), O_RDWR | O_CLOEXEC)};
+ if (fd == -1) fd.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+ ioctl(fd, TIOCSCTTY, 0);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+}
+
+void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
+ std::string published_name = key + name;
+ for (auto& c : published_name) {
+ c = isalnum(c) ? c : '_';
+ }
+
+ std::string val = std::to_string(fd);
+ setenv(published_name.c_str(), val.c_str(), 1);
+}
+
+} // namespace
+
+Result<void> SocketDescriptor::CreateAndPublish(const std::string& global_context) const {
+ const auto& socket_context = context.empty() ? global_context : context;
+ auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
+ if (!result) {
+ return result.error();
+ }
+
+ PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
+
+ return {};
+}
+
+Result<void> FileDescriptor::CreateAndPublish() const {
+ int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
+
+ // Make sure we do not block on open (eg: devices can chose to block on carrier detect). Our
+ // intention is never to delay launch of a service for such a condition. The service can
+ // perform its own blocking on carrier detect.
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
+
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open file '" << name << "'";
+ }
+
+ // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
+ fcntl(fd, F_SETFL, flags);
+
+ LOG(INFO) << "Opened file '" << name << "', flags " << flags;
+
+ PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
+
+ return {};
+}
+
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
+ for (const auto& [nstype, path] : info.namespaces_to_enter) {
+ if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
+ return result;
+ }
+ }
+
+#if defined(__ANDROID__)
+ if (pre_apexd) {
+ if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ return Error() << "could not enter into the bootstrap mount namespace";
+ }
+ }
+#endif
+
+ if (info.flags & CLONE_NEWNS) {
+ bool remount_proc = info.flags & CLONE_NEWPID;
+ bool remount_sys =
+ std::any_of(info.namespaces_to_enter.begin(), info.namespaces_to_enter.end(),
+ [](const auto& entry) { return entry.first == CLONE_NEWNET; });
+ if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result) {
+ return result;
+ }
+ }
+
+ if (info.flags & CLONE_NEWPID) {
+ // This will fork again to run an init process inside the PID namespace.
+ if (auto result = SetUpPidNamespace(name.c_str()); !result) {
+ return result;
+ }
+ }
+
+ return {};
+}
+
+Result<void> SetProcessAttributes(const ProcessAttributes& attr) {
+ if (attr.ioprio_class != IoSchedClass_NONE) {
+ if (android_set_ioprio(getpid(), attr.ioprio_class, attr.ioprio_pri)) {
+ PLOG(ERROR) << "failed to set pid " << getpid() << " ioprio=" << attr.ioprio_class
+ << "," << attr.ioprio_pri;
+ }
+ }
+
+ if (!attr.console.empty()) {
+ setsid();
+ OpenConsole(attr.console);
+ } else {
+ if (setpgid(0, getpid()) == -1) {
+ return ErrnoError() << "setpgid failed";
+ }
+ ZapStdio();
+ }
+
+ for (const auto& rlimit : attr.rlimits) {
+ if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+ return ErrnoError() << StringPrintf(
+ "setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed", rlimit.first,
+ rlimit.second.rlim_cur, rlimit.second.rlim_max);
+ }
+ }
+
+ if (attr.gid) {
+ if (setgid(attr.gid) != 0) {
+ return ErrnoError() << "setgid failed";
+ }
+ }
+ if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {
+ return ErrnoError() << "setgroups failed";
+ }
+ if (attr.uid) {
+ if (setuid(attr.uid) != 0) {
+ return ErrnoError() << "setuid failed";
+ }
+ }
+
+ if (attr.priority != 0) {
+ if (setpriority(PRIO_PROCESS, 0, attr.priority) != 0) {
+ return ErrnoError() << "setpriority failed";
+ }
+ }
+ return {};
+}
+
+Result<void> WritePidToFiles(std::vector<std::string>* files) {
+ // See if there were "writepid" instructions to write to files under cpuset path.
+ std::string cpuset_path;
+ if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
+ auto cpuset_predicate = [&cpuset_path](const std::string& path) {
+ return StartsWith(path, cpuset_path + "/");
+ };
+ auto iter = std::find_if(files->begin(), files->end(), cpuset_predicate);
+ if (iter == files->end()) {
+ // There were no "writepid" instructions for cpusets, check if the system default
+ // cpuset is specified to be used for the process.
+ std::string default_cpuset = GetProperty("ro.cpuset.default", "");
+ if (!default_cpuset.empty()) {
+ // Make sure the cpuset name starts and ends with '/'.
+ // A single '/' means the 'root' cpuset.
+ if (default_cpuset.front() != '/') {
+ default_cpuset.insert(0, 1, '/');
+ }
+ if (default_cpuset.back() != '/') {
+ default_cpuset.push_back('/');
+ }
+ files->push_back(
+ StringPrintf("%s%stasks", cpuset_path.c_str(), default_cpuset.c_str()));
+ }
+ }
+ } else {
+ LOG(ERROR) << "cpuset cgroup controller is not mounted!";
+ }
+ std::string pid_str = std::to_string(getpid());
+ for (const auto& file : *files) {
+ if (!WriteStringToFile(pid_str, file)) {
+ return ErrnoError() << "couldn't write " << pid_str << " to " << file;
+ }
+ }
+ return {};
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service_utils.h b/init/service_utils.h
new file mode 100644
index 0000000..befce25
--- /dev/null
+++ b/init/service_utils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/resource.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <cutils/iosched_policy.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+struct SocketDescriptor {
+ std::string name;
+ int type = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ int perm = 0;
+ std::string context;
+ bool passcred = false;
+
+ Result<void> CreateAndPublish(const std::string& global_context) const;
+};
+
+struct FileDescriptor {
+ std::string name;
+ std::string type;
+
+ Result<void> CreateAndPublish() const;
+};
+
+struct NamespaceInfo {
+ int flags;
+ // Pair of namespace type, path to name.
+ std::vector<std::pair<int, std::string>> namespaces_to_enter;
+};
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
+
+struct ProcessAttributes {
+ std::string console;
+ IoSchedClass ioprio_class;
+ int ioprio_pri;
+ std::vector<std::pair<int, rlimit>> rlimits;
+ uid_t uid;
+ gid_t gid;
+ std::vector<gid_t> supp_gids;
+ int priority;
+};
+Result<void> SetProcessAttributes(const ProcessAttributes& attr);
+
+Result<void> WritePidToFiles(std::vector<std::string>* files);
+
+} // namespace init
+} // namespace android
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 0b03324..984235d 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -29,8 +29,8 @@
#include <android-base/stringprintf.h>
#include "init.h"
-#include "property_service.h"
#include "service.h"
+#include "service_list.h"
using android::base::StringPrintf;
using android::base::boot_clock;
@@ -61,9 +61,7 @@
std::string wait_string;
Service* service = nullptr;
- if (PropertyChildReap(pid)) {
- name = "Async property child";
- } else if (SubcontextChildReap(pid)) {
+ if (SubcontextChildReap(pid)) {
name = "Subcontext";
} else {
service = ServiceList::GetInstance().FindService(pid, &Service::pid);
@@ -75,6 +73,13 @@
auto exec_duration_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+ } else if (service->flags() & SVC_ONESHOT) {
+ auto exec_duration = boot_clock::now() - service->time_started();
+ auto exec_duration_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
+ .count();
+ wait_string = StringPrintf(" oneshot service took %f seconds in background",
+ exec_duration_ms / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 092c51c..00f91d8 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,11 +27,13 @@
#include <selinux/android.h>
#include "action.h"
+#include "builtins.h"
#include "util.h"
#if defined(__ANDROID__)
#include <android/api-level.h>
#include "property_service.h"
+#include "selabel.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
@@ -71,7 +73,7 @@
}
template <typename T>
-Result<Success> SendMessage(int socket, const T& message) {
+Result<void> SendMessage(int socket, const T& message) {
std::string message_string;
if (!message.SerializeToString(&message_string)) {
return Error() << "Unable to serialize message";
@@ -86,7 +88,7 @@
result != static_cast<long>(message_string.size())) {
return ErrnoError() << "send() failed to send message contents";
}
- return Success();
+ return {};
}
std::vector<std::pair<std::string, std::string>> properties_to_set;
@@ -98,7 +100,7 @@
class SubcontextProcess {
public:
- SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+ SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
: function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
void MainLoop();
@@ -108,7 +110,7 @@
void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
SubcontextReply* reply) const;
- const KeywordFunctionMap* function_map_;
+ const BuiltinFunctionMap* function_map_;
const std::string context_;
const int init_fd_;
};
@@ -121,12 +123,12 @@
args.emplace_back(string);
}
- auto map_result = function_map_->FindFunction(args);
- Result<Success> result;
+ auto map_result = function_map_->Find(args);
+ Result<void> result;
if (!map_result) {
result = Error() << "Cannot find command: " << map_result.error();
} else {
- result = RunBuiltinFunction(map_result->second, args, context_);
+ result = RunBuiltinFunction(map_result->function, args, context_);
}
for (const auto& [name, value] : properties_to_set) {
@@ -141,23 +143,23 @@
reply->set_success(true);
} else {
auto* failure = reply->mutable_failure();
- failure->set_error_string(result.error_string());
- failure->set_error_errno(result.error_errno());
+ failure->set_error_string(result.error().message());
+ failure->set_error_errno(result.error().code());
}
}
void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
SubcontextReply* reply) const {
for (const auto& arg : expand_args_command.args()) {
- auto expanded_prop = std::string{};
- if (!expand_props(arg, &expanded_prop)) {
+ auto expanded_arg = ExpandProps(arg);
+ if (!expanded_arg) {
auto* failure = reply->mutable_failure();
- failure->set_error_string("Failed to expand '" + arg + "'");
+ failure->set_error_string(expanded_arg.error().message());
failure->set_error_errno(0);
return;
} else {
auto* expand_args_reply = reply->mutable_expand_args_reply();
- expand_args_reply->add_expanded_args(expanded_prop);
+ expand_args_reply->add_expanded_args(*expanded_arg);
}
}
}
@@ -177,7 +179,7 @@
auto init_message = ReadMessage(init_fd_);
if (!init_message) {
- if (init_message.error_errno() == 0) {
+ if (init_message.error().code() == 0) {
// If the init file descriptor was closed, let's exit quietly. If
// this was accidental, init will restart us. If init died, this
// avoids calling abort(3) unnecessarily.
@@ -214,7 +216,7 @@
} // namespace
-int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) {
if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
auto context = std::string(argv[2]);
@@ -245,7 +247,7 @@
// We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
// in the subcontext process after we exec.
- int child_fd = dup(subcontext_socket);
+ int child_fd = dup(subcontext_socket); // NOLINT(android-cloexec-dup)
if (child_fd < 0) {
PLOG(FATAL) << "Could not dup child_fd";
}
@@ -298,7 +300,7 @@
return subcontext_reply;
}
-Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
auto subcontext_command = SubcontextCommand();
std::copy(
args.begin(), args.end(),
@@ -328,7 +330,7 @@
<< subcontext_reply->reply_case();
}
- return Success();
+ return {};
}
Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
diff --git a/init/subcontext.h b/init/subcontext.h
index 628fd50..591521f 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -42,7 +42,7 @@
Fork();
}
- Result<Success> Execute(const std::vector<std::string>& args);
+ Result<void> Execute(const std::vector<std::string>& args);
Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
void Restart();
@@ -60,7 +60,7 @@
android::base::unique_fd socket_;
};
-int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
std::vector<Subcontext>* InitializeSubcontexts();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index eae03e3..f6fee8a 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -19,8 +19,6 @@
#include <benchmark/benchmark.h>
#include <selinux/selinux.h>
-#include "test_function_map.h"
-
namespace android {
namespace init {
@@ -39,7 +37,7 @@
free(context);
while (state.KeepRunning()) {
- subcontext.Execute(std::vector<std::string>{"return_success"}).IgnoreError();
+ subcontext.Execute(std::vector<std::string>{"return_success"});
}
if (subcontext.pid() > 0) {
@@ -50,11 +48,11 @@
BENCHMARK(BenchmarkSuccess);
-TestFunctionMap BuildTestFunctionMap() {
- TestFunctionMap test_function_map;
- test_function_map.Add("return_success", 0, 0, true,
- [](const BuiltinArguments& args) { return Success(); });
-
+BuiltinFunctionMap BuildTestFunctionMap() {
+ auto function = [](const BuiltinArguments& args) { return Result<void>{}; };
+ BuiltinFunctionMap test_function_map = {
+ {"return_success", {0, 0, {true, function}}},
+ };
return test_function_map;
}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 230203a..ae89c38 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -26,7 +26,6 @@
#include <selinux/selinux.h>
#include "builtin_arguments.h"
-#include "test_function_map.h"
using namespace std::literals;
@@ -69,7 +68,7 @@
auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
ASSERT_FALSE(result);
- auto pids = Split(result.error_string(), " ");
+ auto pids = Split(result.error().message(), " ");
ASSERT_EQ(2U, pids.size());
auto our_pid = std::to_string(getpid());
EXPECT_NE(our_pid, pids[0]);
@@ -116,7 +115,7 @@
auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
ASSERT_FALSE(result);
- EXPECT_EQ(Join(expected_words, " "), result.error_string());
+ EXPECT_EQ(Join(expected_words, " "), result.error().message());
EXPECT_EQ(first_pid, subcontext.pid());
});
}
@@ -130,7 +129,7 @@
auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
ASSERT_FALSE(result2);
- EXPECT_EQ("Sane error!", result2.error_string());
+ EXPECT_EQ("Sane error!", result2.error().message());
EXPECT_NE(subcontext.pid(), first_pid);
});
}
@@ -139,7 +138,7 @@
RunTest([](auto& subcontext, auto& context_string) {
auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
ASSERT_FALSE(result);
- ASSERT_EQ(context_string, result.error_string());
+ ASSERT_EQ(context_string, result.error().message());
});
}
@@ -167,50 +166,58 @@
};
auto result = subcontext.ExpandArgs(args);
ASSERT_FALSE(result);
- EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
+ EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
+ result.error().message());
});
}
-TestFunctionMap BuildTestFunctionMap() {
- TestFunctionMap test_function_map;
+BuiltinFunctionMap BuildTestFunctionMap() {
// For CheckDifferentPid
- test_function_map.Add("return_pids_as_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> {
- return Error() << getpid() << " " << getppid();
- });
+ auto do_return_pids_as_error = [](const BuiltinArguments& args) -> Result<void> {
+ return Error() << getpid() << " " << getppid();
+ };
// For SetProp
- test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+ auto do_setprop = [](const BuiltinArguments& args) {
android::base::SetProperty(args[1], args[2]);
- return Success();
- });
+ return Result<void>{};
+ };
// For MultipleCommands
// Using a shared_ptr to extend lifetime of words to both lambdas
auto words = std::make_shared<std::vector<std::string>>();
- test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+ auto do_add_word = [words](const BuiltinArguments& args) {
words->emplace_back(args[1]);
- return Success();
- });
- test_function_map.Add("return_words_as_error", 0, 0, true,
- [words](const BuiltinArguments& args) -> Result<Success> {
- return Error() << Join(*words, " ");
- });
+ return Result<void>{};
+ };
+ auto do_return_words_as_error = [words](const BuiltinArguments& args) -> Result<void> {
+ return Error() << Join(*words, " ");
+ };
// For RecoverAfterAbort
- test_function_map.Add("cause_log_fatal", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> {
- return Error() << std::string(4097, 'f');
- });
- test_function_map.Add(
- "generate_sane_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+ auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
+ return Error() << std::string(4097, 'f');
+ };
+ auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
+ return Error() << "Sane error!";
+ };
// For ContextString
- test_function_map.Add(
- "return_context_as_error", 0, 0, true,
- [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+ auto do_return_context_as_error = [](const BuiltinArguments& args) -> Result<void> {
+ return Error() << args.context;
+ };
+ // clang-format off
+ BuiltinFunctionMap test_function_map = {
+ {"return_pids_as_error", {0, 0, {true, do_return_pids_as_error}}},
+ {"setprop", {2, 2, {true, do_setprop}}},
+ {"add_word", {1, 1, {true, do_add_word}}},
+ {"return_words_as_error", {0, 0, {true, do_return_words_as_error}}},
+ {"cause_log_fatal", {0, 0, {true, do_cause_log_fatal}}},
+ {"generate_sane_error", {0, 0, {true, do_generate_sane_error}}},
+ {"return_context_as_error", {0, 0, {true, do_return_context_as_error}}},
+ };
+ // clang-format on
return test_function_map;
}
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 0e59b57..575b67f 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -16,7 +16,6 @@
#include "switch_root.h"
-#include <dirent.h>
#include <fcntl.h>
#include <mntent.h>
#include <sys/mount.h>
@@ -35,45 +34,6 @@
namespace {
-void FreeRamdisk(DIR* dir, dev_t dev) {
- int dfd = dirfd(dir);
-
- dirent* de;
- while ((de = readdir(dir)) != nullptr) {
- if (de->d_name == "."s || de->d_name == ".."s) {
- continue;
- }
-
- bool is_dir = false;
-
- if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
- struct stat info;
- if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
- continue;
- }
-
- if (info.st_dev != dev) {
- continue;
- }
-
- if (S_ISDIR(info.st_mode)) {
- is_dir = true;
- auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
- if (fd >= 0) {
- auto subdir =
- std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
- if (subdir) {
- FreeRamdisk(subdir.get(), dev);
- } else {
- close(fd);
- }
- }
- }
- }
- unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
- }
-}
-
std::vector<std::string> GetMounts(const std::string& new_root) {
auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent("/proc/mounts", "re"),
endmntent};
@@ -112,24 +72,16 @@
void SwitchRoot(const std::string& new_root) {
auto mounts = GetMounts(new_root);
+ LOG(INFO) << "Switching root to '" << new_root << "'";
+
for (const auto& mount_path : mounts) {
auto new_mount_path = new_root + mount_path;
+ mkdir(new_mount_path.c_str(), 0755);
if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
}
}
- auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
- if (!old_root_dir) {
- PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
- }
-
- struct stat old_root_info;
- if (stat("/", &old_root_info) != 0) {
- PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
- old_root_dir.reset();
- }
-
if (chdir(new_root.c_str()) != 0) {
PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
}
@@ -141,10 +93,6 @@
if (chroot(".") != 0) {
PLOG(FATAL) << "Unable to chroot to new root";
}
-
- if (old_root_dir) {
- FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
- }
}
} // namespace init
diff --git a/init/test_function_map.h b/init/test_function_map.h
deleted file mode 100644
index 583df1a..0000000
--- a/init/test_function_map.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _INIT_TEST_FUNCTION_MAP_H
-#define _INIT_TEST_FUNCTION_MAP_H
-
-#include <string>
-#include <vector>
-
-#include "builtin_arguments.h"
-#include "keyword_map.h"
-
-namespace android {
-namespace init {
-
-class TestFunctionMap : public KeywordFunctionMap {
- public:
- // Helper for argument-less functions
- using BuiltinFunctionNoArgs = std::function<void(void)>;
- void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
- Add(name, 0, 0, false, [function](const BuiltinArguments&) {
- function();
- return Success();
- });
- }
-
- void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
- bool run_in_subcontext, const BuiltinFunction function) {
- builtin_functions_[name] =
- make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
- }
-
- private:
- Map builtin_functions_ = {};
-
- const Map& map() const override { return builtin_functions_; }
-};
-
-} // namespace init
-} // namespace android
-
-#endif
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
index acfc7c7..6b31683 100644
--- a/init/tokenizer_test.cpp
+++ b/init/tokenizer_test.cpp
@@ -46,6 +46,7 @@
return;
case T_NEWLINE:
tokens.emplace_back(std::move(current_line));
+ current_line.clear();
break;
case T_TEXT:
current_line.emplace_back(state.text);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 8cf2128..ac633776 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -86,9 +86,8 @@
}
}
-UeventListener::UeventListener() {
- // is 2MB enough? udev uses 128MB!
- device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
+UeventListener::UeventListener(size_t uevent_socket_rcvbuf_size) {
+ device_fd_.reset(uevent_open_socket(uevent_socket_rcvbuf_size, true));
if (device_fd_ == -1) {
LOG(FATAL) << "Could not open uevent socket";
}
@@ -132,7 +131,7 @@
const ListenerCallback& callback) const {
int dfd = dirfd(d);
- int fd = openat(dfd, "uevent", O_WRONLY);
+ int fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
if (fd >= 0) {
write(fd, "add\n", 4);
close(fd);
@@ -147,7 +146,7 @@
while ((de = readdir(d)) != nullptr) {
if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
- fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+ fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (fd < 0) continue;
std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
diff --git a/init/uevent_listener.h b/init/uevent_listener.h
index 5b453fe..aea094e 100644
--- a/init/uevent_listener.h
+++ b/init/uevent_listener.h
@@ -41,7 +41,7 @@
class UeventListener {
public:
- UeventListener();
+ UeventListener(size_t uevent_socket_rcvbuf_size);
void RegenerateUevents(const ListenerCallback& callback) const;
ListenerAction RegenerateUeventsForPath(const std::string& path,
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 66491dd..8b2cf62 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -37,6 +37,7 @@
#include "devices.h"
#include "firmware_handler.h"
#include "modalias_handler.h"
+#include "selabel.h"
#include "selinux.h"
#include "uevent_handler.h"
#include "uevent_listener.h"
@@ -145,7 +146,7 @@
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
- uevent_queue_.emplace_back(std::move(uevent));
+ uevent_queue_.emplace_back(uevent);
return ListenerAction::kContinue;
});
}
@@ -213,7 +214,7 @@
WaitForSubProcesses();
- close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
+ android::base::SetProperty(kColdBootDoneProp, "true");
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
@@ -233,31 +234,29 @@
SelabelInitialize();
std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
- UeventListener uevent_listener;
- {
- // Keep the current product name base configuration so we remain backwards compatible and
- // allow it to override everything.
- // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
- auto hardware = android::base::GetProperty("ro.hardware", "");
+ // Keep the current product name base configuration so we remain backwards compatible and
+ // allow it to override everything.
+ // TODO: cleanup platform ueventd.rc to remove vendor specific device node entries (b/34968103)
+ auto hardware = android::base::GetProperty("ro.hardware", "");
- auto ueventd_configuration =
- ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
- "/ueventd." + hardware + ".rc"});
+ auto ueventd_configuration = ParseConfig({"/ueventd.rc", "/vendor/ueventd.rc",
+ "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
- uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
- std::move(ueventd_configuration.dev_permissions),
- std::move(ueventd_configuration.sysfs_permissions),
- std::move(ueventd_configuration.subsystems), fs_mgr_get_boot_devices(), true));
- uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
- std::move(ueventd_configuration.firmware_directories)));
+ uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+ std::move(ueventd_configuration.dev_permissions),
+ std::move(ueventd_configuration.sysfs_permissions),
+ std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
+ uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
+ std::move(ueventd_configuration.firmware_directories)));
- if (ueventd_configuration.enable_modalias_handling) {
- uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
- }
+ if (ueventd_configuration.enable_modalias_handling) {
+ std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
+ uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
}
+ UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
- if (access(COLDBOOT_DONE, F_OK) != 0) {
+ if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers);
cold_boot.Run();
}
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 677938e..8ee0cce 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -19,15 +19,19 @@
#include <grp.h>
#include <pwd.h>
+#include <android-base/parseint.h>
+
#include "keyword_map.h"
#include "parser.h"
+using android::base::ParseByteCount;
+
namespace android {
namespace init {
-Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
- std::vector<SysfsPermissions>* out_sysfs_permissions,
- std::vector<Permissions>* out_dev_permissions) {
+Result<void> ParsePermissionsLine(std::vector<std::string>&& args,
+ std::vector<SysfsPermissions>* out_sysfs_permissions,
+ std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
if (is_sysfs && args.size() != 5) {
return Error() << "/sys/ lines must have 5 entries";
@@ -70,22 +74,22 @@
} else {
out_dev_permissions->emplace_back(name, perm, uid, gid);
}
- return Success();
+ return {};
}
-Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
- std::vector<std::string>* firmware_directories) {
+Result<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+ std::vector<std::string>* firmware_directories) {
if (args.size() < 2) {
return Error() << "firmware_directories must have at least 1 entry";
}
std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
- return Success();
+ return {};
}
-Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
- bool* enable_modalias_handling) {
+Result<void> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+ bool* enable_modalias_handling) {
if (args.size() != 2) {
return Error() << "modalias_handling lines take exactly one parameter";
}
@@ -98,27 +102,43 @@
return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
}
- return Success();
+ return {};
+}
+
+Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
+ size_t* uevent_socket_rcvbuf_size) {
+ if (args.size() != 2) {
+ return Error() << "uevent_socket_rcvbuf_size lines take exactly one parameter";
+ }
+
+ size_t parsed_size;
+ if (!ParseByteCount(args[1], &parsed_size)) {
+ return Error() << "could not parse size '" << args[1] << "' for uevent_socket_rcvbuf_line";
+ }
+
+ *uevent_socket_rcvbuf_size = parsed_size;
+
+ return {};
}
class SubsystemParser : public SectionParser {
public:
SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}
- Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
- int line) override;
- Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
- Result<Success> EndSection() override;
+ Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+ Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
+ Result<void> EndSection() override;
private:
- Result<Success> ParseDevName(std::vector<std::string>&& args);
- Result<Success> ParseDirName(std::vector<std::string>&& args);
+ Result<void> ParseDevName(std::vector<std::string>&& args);
+ Result<void> ParseDirName(std::vector<std::string>&& args);
Subsystem subsystem_;
std::vector<Subsystem>* subsystems_;
};
-Result<Success> SubsystemParser::ParseSection(std::vector<std::string>&& args,
- const std::string& filename, int line) {
+Result<void> SubsystemParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "subsystems must have exactly one name";
}
@@ -129,58 +149,51 @@
subsystem_ = Subsystem(std::move(args[1]));
- return Success();
+ return {};
}
-Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
if (args[1] == "uevent_devname") {
subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
- return Success();
+ return {};
}
if (args[1] == "uevent_devpath") {
subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
- return Success();
+ return {};
}
return Error() << "invalid devname '" << args[1] << "'";
}
-Result<Success> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
+Result<void> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
if (args[1].front() != '/') {
return Error() << "dirname '" << args[1] << " ' does not start with '/'";
}
subsystem_.dir_name_ = args[1];
- return Success();
+ return {};
}
-Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
- using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
+Result<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+ using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);
+ // clang-format off
+ static const KeywordMap<OptionParser> parser_map = {
+ {"devname", {1, 1, &SubsystemParser::ParseDevName}},
+ {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
+ };
+ // clang-format on
- static class OptionParserMap : public KeywordMap<OptionParser> {
- private:
- const Map& map() const override {
- // clang-format off
- static const Map option_parsers = {
- {"devname", {1, 1, &SubsystemParser::ParseDevName}},
- {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
- };
- // clang-format on
- return option_parsers;
- }
- } parser_map;
-
- auto parser = parser_map.FindFunction(args);
+ auto parser = parser_map.Find(args);
if (!parser) return Error() << parser.error();
return std::invoke(*parser, this, std::move(args));
}
-Result<Success> SubsystemParser::EndSection() {
+Result<void> SubsystemParser::EndSection() {
subsystems_->emplace_back(std::move(subsystem_));
- return Success();
+ return {};
}
UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
@@ -202,6 +215,9 @@
parser.AddSingleLineParser("modalias_handling",
std::bind(ParseModaliasHandlingLine, _1,
&ueventd_configuration.enable_modalias_handling));
+ parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
+ std::bind(ParseUeventSocketRcvbufSizeLine, _1,
+ &ueventd_configuration.uevent_socket_rcvbuf_size));
for (const auto& config : configs) {
parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 7d30edf..d476dec 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -31,6 +31,7 @@
std::vector<Permissions> dev_permissions;
std::vector<std::string> firmware_directories;
bool enable_modalias_handling = false;
+ size_t uevent_socket_rcvbuf_size = 0;
};
UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 31208b9..9c1cedf 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -16,7 +16,7 @@
#include "ueventd_parser.h"
-#include <android-base/test_utils.h>
+#include <android-base/file.h>
#include <gtest/gtest.h>
#include <private/android_filesystem_config.h>
@@ -138,6 +138,15 @@
TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
}
+TEST(ueventd_parser, UeventSocketRcvbufSize) {
+ auto ueventd_file = R"(
+uevent_socket_rcvbuf_size 8k
+uevent_socket_rcvbuf_size 8M
+)";
+
+ TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
+}
+
TEST(ueventd_parser, AllTogether) {
auto ueventd_file = R"(
@@ -169,6 +178,8 @@
/sys/devices/virtual/*/input poll_delay 0660 root input
firmware_directories /more
+uevent_socket_rcvbuf_size 6M
+
#ending comment
)";
@@ -197,8 +208,10 @@
"/more",
};
- TestUeventdFile(ueventd_file,
- {subsystems, sysfs_permissions, permissions, firmware_directories});
+ size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
+
+ TestUeventdFile(ueventd_file, {subsystems, sysfs_permissions, permissions, firmware_directories,
+ false, uevent_socket_rcvbuf_size});
}
// All of these lines are ill-formed, so test that there is 0 output.
@@ -213,6 +226,8 @@
/sys/devices/platform/trusty.* trusty_version 0440 baduidbad log
/sys/devices/platform/trusty.* trusty_version 0440 root baduidbad
+uevent_socket_rcvbuf_size blah
+
subsystem #no name
)";
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 7290051..bfdc28e 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -27,7 +27,6 @@
#include <android-base/file.h>
#include <android-base/scopeguard.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <selinux/android.h>
#include <selinux/label.h>
diff --git a/init/util.cpp b/init/util.cpp
index 3781141..0532375 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -34,23 +34,25 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <selinux/android.h>
#if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
+#include "reboot_utils.h"
+#include "selabel.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
-#ifdef _INIT_INIT_H
-#error "Do not include init.h in files used by ueventd; it will expose init's globals"
-#endif
-
using android::base::boot_clock;
+using android::base::StartsWith;
using namespace std::literals::string_literals;
namespace android {
@@ -81,32 +83,28 @@
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon) {
- if (socketcon) {
- if (setsockcreatecon(socketcon) == -1) {
- PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
- return -1;
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+ gid_t gid, const std::string& socketcon) {
+ if (!socketcon.empty()) {
+ if (setsockcreatecon(socketcon.c_str()) == -1) {
+ return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
}
}
android::base::unique_fd fd(socket(PF_UNIX, type, 0));
if (fd < 0) {
- PLOG(ERROR) << "Failed to open socket '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to open socket '" << name << "'";
}
- if (socketcon) setsockcreatecon(NULL);
+ if (!socketcon.empty()) setsockcreatecon(nullptr);
struct sockaddr_un addr;
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
- name);
+ snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());
if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
- PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to unlink old socket '" << name << "'";
}
std::string secontext;
@@ -117,8 +115,7 @@
if (passcred) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
- PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
- return -1;
+ return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
}
}
@@ -129,19 +126,18 @@
setfscreatecon(nullptr);
}
+ auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });
+
if (ret) {
errno = savederrno;
- PLOG(ERROR) << "Failed to bind socket '" << name << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to bind socket '" << name << "'";
}
if (lchown(addr.sun_path, uid, gid)) {
- PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";
}
if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
- PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
- goto out_unlink;
+ return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
}
LOG(INFO) << "Created socket '" << addr.sun_path << "'"
@@ -149,11 +145,8 @@
<< ", user " << uid
<< ", group " << gid;
+ guard.Disable();
return fd.release();
-
-out_unlink:
- unlink(addr.sun_path);
- return -1;
}
Result<std::string> ReadFile(const std::string& path) {
@@ -197,7 +190,7 @@
return rc;
}
-Result<Success> WriteFile(const std::string& path, const std::string& content) {
+Result<void> WriteFile(const std::string& path, const std::string& content) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
@@ -206,7 +199,7 @@
if (!android::base::WriteStringToFd(content, fd)) {
return ErrnoError() << "Unable to write file contents";
}
- return Success();
+ return {};
}
bool mkdir_recursive(const std::string& path, mode_t mode) {
@@ -269,16 +262,6 @@
}
/*
- * Writes hex_len hex characters (1/2 byte) to hex from bytes.
- */
-std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
- std::string hex("0x");
- for (size_t i = 0; i < bytes_len; i++)
- android::base::StringAppendF(&hex, "%02x", bytes[i]);
- return hex;
-}
-
-/*
* Returns true is pathname is a directory
*/
bool is_dir(const char* pathname) {
@@ -289,12 +272,10 @@
return S_ISDIR(info.st_mode);
}
-bool expand_props(const std::string& src, std::string* dst) {
+Result<std::string> ExpandProps(const std::string& src) {
const char* src_ptr = src.c_str();
- if (!dst) {
- return false;
- }
+ std::string dst;
/* - variables can either be $x.y or ${x.y}, in case they are only part
* of the string.
@@ -308,19 +289,19 @@
c = strchr(src_ptr, '$');
if (!c) {
- dst->append(src_ptr);
- return true;
+ dst.append(src_ptr);
+ return dst;
}
- dst->append(src_ptr, c);
+ dst.append(src_ptr, c);
c++;
if (*c == '$') {
- dst->push_back(*(c++));
+ dst.push_back(*(c++));
src_ptr = c;
continue;
} else if (*c == '\0') {
- return true;
+ return dst;
}
std::string prop_name;
@@ -330,8 +311,7 @@
const char* end = strchr(c, '}');
if (!end) {
// failed to find closing brace, abort.
- LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
- return false;
+ return Error() << "unexpected end of string in '" << src << "', looking for }";
}
prop_name = std::string(c, end);
c = end + 1;
@@ -342,29 +322,34 @@
}
} else {
prop_name = c;
- LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
+ if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+ return Error() << "using deprecated syntax for specifying property '" << c
+ << "', use ${name} instead";
+ } else {
+ LOG(ERROR) << "using deprecated syntax for specifying property '" << c
+ << "', use ${name} instead";
+ }
c += prop_name.size();
}
if (prop_name.empty()) {
- LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
- return false;
+ return Error() << "invalid zero-length property name in '" << src << "'";
}
std::string prop_val = android::base::GetProperty(prop_name, "");
if (prop_val.empty()) {
if (def_val.empty()) {
- LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
- return false;
+ return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
+ << src << "'";
}
prop_val = def_val;
}
- dst->append(prop_val);
+ dst.append(prop_val);
src_ptr = c;
}
- return true;
+ return dst;
}
static std::string init_android_dt_dir() {
@@ -436,20 +421,106 @@
return true;
}
-void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function) {
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value) {
+ if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
+ return Error() << "Property value too long";
+ }
+
+ if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+ return Error() << "Value is not a UTF8 encoded string";
+ }
+
+ return {};
+}
+
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+ const std::vector<std::string>& args) {
+ struct flag_type {
+ const char* name;
+ int value;
+ };
+ static const flag_type flags[] = {
+ {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
+ {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
+ {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+ {0, 0}};
+
+ int flag = 0;
+ std::vector<std::string> paths;
+
+ bool in_flags = true;
+ for (size_t i = 1; i < args.size(); ++i) {
+ if (android::base::StartsWith(args[i], "--")) {
+ if (!in_flags) {
+ return Error() << "flags must precede paths";
+ }
+ bool found = false;
+ for (size_t j = 0; flags[j].name; ++j) {
+ if (args[i] == flags[j].name) {
+ flag |= flags[j].value;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return Error() << "bad flag " << args[i];
+ }
+ } else {
+ in_flags = false;
+ paths.emplace_back(args[i]);
+ }
+ }
+ return std::pair(flag, paths);
+}
+
+static void InitAborter(const char* abort_message) {
+ // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
+ // simply abort instead of trying to reboot the system.
+ if (getpid() != 1) {
+ android::base::DefaultAborter(abort_message);
+ return;
+ }
+
+ InitFatalReboot();
+}
+
+// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
+// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr.
+// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with
+// /dev/null regardless.
+//
+// In the case that these fds are provided by the kernel, the exec of second stage init causes an
+// SELinux denial as it does not have access to /dev/console. In the case that they are not
+// provided, exec of any further process is potentially dangerous as the first fd's opened by that
+// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is
+// then used by that process.
+//
+// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first
+// stage init still runs in kernel context, future child processes will not have permissions to
+// access any fds that it opens, including the one opened below for /dev/null. Therefore,
+// SetStdioToDevNull() must be called again in second stage init.
+void SetStdioToDevNull(char** argv) {
// Make stdin/stdout/stderr all point to /dev/null.
- int fd = open("/dev/null", O_RDWR);
+ int fd = open("/dev/null", O_RDWR); // NOLINT(android-cloexec-open)
if (fd == -1) {
int saved_errno = errno;
- android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+ android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
errno = saved_errno;
PLOG(FATAL) << "Couldn't open /dev/null";
}
- dup2(fd, 0);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd > 2) close(fd);
- android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO) close(fd);
+}
+
+void InitKernelLogging(char** argv) {
+ SetFatalRebootTarget();
+ android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
+}
+
+bool IsRecoveryMode() {
+ return access("/system/bin/recovery", F_OK) == 0;
}
} // namespace init
diff --git a/init/util.h b/init/util.h
index 53f4547..4cccefe 100644
--- a/init/util.h
+++ b/init/util.h
@@ -14,35 +14,31 @@
* limitations under the License.
*/
-#ifndef _INIT_UTIL_H_
-#define _INIT_UTIL_H_
+#pragma once
#include <sys/stat.h>
#include <sys/types.h>
#include <chrono>
#include <functional>
-#include <ostream>
#include <string>
#include <android-base/chrono_utils.h>
-#include <selinux/label.h>
#include "result.h"
-#define COLDBOOT_DONE "/dev/.coldboot_done"
-
using android::base::boot_clock;
-using namespace std::chrono_literals;
namespace android {
namespace init {
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
- const char* socketcon);
+static const char kColdBootDoneProp[] = "ro.cold_boot_done";
+
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+ gid_t gid, const std::string& socketcon);
Result<std::string> ReadFile(const std::string& path);
-Result<Success> WriteFile(const std::string& path, const std::string& content);
+Result<void> WriteFile(const std::string& path, const std::string& content);
Result<uid_t> DecodeUid(const std::string& name);
@@ -51,9 +47,8 @@
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
bool make_dir(const std::string& path, mode_t mode);
-std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
bool is_dir(const char* pathname);
-bool expand_props(const std::string& src, std::string* dst);
+Result<std::string> ExpandProps(const std::string& src);
// Returns the platform's Android DT directory as specified in the kernel cmdline.
// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
@@ -63,10 +58,13 @@
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
bool IsLegalPropertyName(const std::string& name);
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
-void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+ const std::vector<std::string>& args);
+void SetStdioToDevNull(char** argv);
+void InitKernelLogging(char** argv);
+bool IsRecoveryMode();
} // namespace init
} // namespace android
-
-#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 3ae53a4..8947256 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -20,8 +20,8 @@
#include <fcntl.h>
#include <sys/stat.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
using namespace std::literals::string_literals;
@@ -34,7 +34,7 @@
auto file_contents = ReadFile("/proc/does-not-exist");
EXPECT_EQ(ENOENT, errno);
ASSERT_FALSE(file_contents);
- EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
+ EXPECT_EQ("open() failed: No such file or directory", file_contents.error().message());
}
TEST(util, ReadFileGroupWriteable) {
@@ -45,7 +45,7 @@
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
- EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+ EXPECT_EQ("Skipping insecure file", file_contents.error().message());
}
TEST(util, ReadFileWorldWiteable) {
@@ -56,7 +56,7 @@
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
- EXPECT_EQ("Skipping insecure file", file_contents.error_string());
+ EXPECT_EQ("Skipping insecure file", file_contents.error().message());
}
TEST(util, ReadFileSymbolicLink) {
@@ -65,7 +65,8 @@
auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents);
- EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
+ EXPECT_EQ("open() failed: Too many symbolic links encountered",
+ file_contents.error().message());
}
TEST(util, ReadFileSuccess) {
@@ -130,7 +131,7 @@
decoded_uid = DecodeUid("toot");
EXPECT_FALSE(decoded_uid);
- EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
+ EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error().message());
decoded_uid = DecodeUid("123");
EXPECT_TRUE(decoded_uid);
diff --git a/janitors/OWNERS b/janitors/OWNERS
new file mode 100644
index 0000000..3e32c26
--- /dev/null
+++ b/janitors/OWNERS
@@ -0,0 +1,6 @@
+# OWNERS file for projects that don't really have owners so much as volunteer janitors.
+ccross@google.com
+dwillemsen@google.com
+enh@google.com
+hhb@google.com
+narayan@google.com
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 8b0c53e..f1ca446 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -311,7 +311,7 @@
};
FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
- base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
+ base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd.get() == -1) {
PLOG(ERROR) << "Failed to open FD for epoll";
opened_ = false;
@@ -353,8 +353,8 @@
}
if (entry->IsClosing()) {
const int mount_id = entry->mount_id();
- callback->OnClosed(mount_id);
bridges_.erase(mount_id);
+ callback->OnClosed(mount_id);
if (bridges_.size() == 0) {
// All bridges are now closed.
return false;
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7f9a18a..565f2c3 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,6 +42,7 @@
name: "libbacktrace_headers",
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
}
@@ -96,7 +97,6 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
},
},
- whole_static_libs: ["libdemangle"],
}
cc_test_library {
@@ -134,7 +134,6 @@
defaults: ["libbacktrace_common"],
host_supported: true,
srcs: [
- "backtrace_offline_test.cpp",
"backtrace_test.cpp",
],
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 6bec63c..3e050ab 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -28,13 +28,13 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <demangle.h>
-
#include "BacktraceLog.h"
#include "UnwindStack.h"
using android::base::StringPrintf;
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
//-------------------------------------------------------------------------
// Backtrace functions.
//-------------------------------------------------------------------------
@@ -63,7 +63,14 @@
if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
return "";
}
- return demangle(GetFunctionNameRaw(pc, offset).c_str());
+ std::string name(GetFunctionNameRaw(pc, offset));
+ char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ name = demangled_name;
+ free(demangled_name);
+ return name;
+ }
+ return name;
}
bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
@@ -170,5 +177,7 @@
return "Failed to unwind due to invalid unwind information";
case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
return "Failed to unwind due to same sp/pc repeating";
+ case BACKTRACE_UNWIND_ERROR_INVALID_ELF:
+ return "Failed to unwind due to invalid elf";
}
}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 39cb995..038b59e 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -76,7 +76,7 @@
return UnwindFromContext(num_ignore_frames, ucontext);
}
- if (Tid() != android::base::GetThreadId()) {
+ if (Tid() != static_cast<pid_t>(android::base::GetThreadId())) {
return UnwindThread(num_ignore_frames);
}
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index 6a967f7..781819a 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -46,8 +46,7 @@
}
}
-BacktraceMap::~BacktraceMap() {
-}
+BacktraceMap::~BacktraceMap() {}
void BacktraceMap::FillIn(uint64_t addr, backtrace_map_t* map) {
ScopedBacktraceMapIteratorLock lock(this);
@@ -68,12 +67,13 @@
char permissions[5];
int name_pos;
-// Mac OS vmmap(1) output:
-// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0 1 2 3 4 5
- if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n",
- &start, &end, permissions, &name_pos) != 3) {
+ // Mac OS vmmap(1) output:
+ // __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW
+ // /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
+ // 012345678901234567890123456789012345678901234567890123456789
+ // 0 1 2 3 4 5
+ if (sscanf(line, "%*21c %" SCNx64 "-%" SCNx64 " [%*13c] %3c/%*3c SM=%*3c %n", &start, &end,
+ permissions, &name_pos) != 3) {
return false;
}
@@ -90,21 +90,21 @@
map->flags |= PROT_EXEC;
}
- map->name = line+name_pos;
- if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
- map->name.erase(map->name.length()-1);
+ map->name = line + name_pos;
+ if (!map->name.empty() && map->name[map->name.length() - 1] == '\n') {
+ map->name.erase(map->name.length() - 1);
}
- ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s",
- reinterpret_cast<void*>(map->start), reinterpret_cast<void*>(map->end),
- map->flags, map->name.c_str());
+ ALOGV("Parsed map: start=%p, end=%p, flags=%x, name=%s", reinterpret_cast<void*>(map->start),
+ reinterpret_cast<void*>(map->end), map->flags, map->name.c_str());
return true;
}
#endif // defined(__APPLE__)
bool BacktraceMap::Build() {
#if defined(__APPLE__)
- char cmd[sizeof(pid_t)*3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
+ char
+ cmd[sizeof(pid_t) * 3 + sizeof("vmmap -w -resident -submap -allSplitLibs -interleaved ") + 1];
char line[1024];
// cmd is guaranteed to always be big enough to hold this string.
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
@@ -113,7 +113,7 @@
return false;
}
- while(fgets(line, sizeof(line), fp)) {
+ while (fgets(line, sizeof(line), fp)) {
backtrace_map_t map;
if (ParseLine(line, &map)) {
maps_.push_back(map);
@@ -123,7 +123,7 @@
return true;
#else
return android::procinfo::ReadProcessMaps(
- pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+ pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t, const char* name) {
maps_.resize(maps_.size() + 1);
backtrace_map_t& map = maps_.back();
map.start = start;
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index fe28eba..624711f 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -24,7 +24,6 @@
#include <string>
#include <backtrace/Backtrace.h>
-#include <demangle.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
@@ -41,6 +40,8 @@
#include "UnwindStack.h"
#include "UnwindStackMap.h"
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
@@ -89,6 +90,10 @@
case unwindstack::ERROR_REPEATED_FRAME:
error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
break;
+
+ case unwindstack::ERROR_INVALID_ELF:
+ error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
+ break;
}
}
@@ -111,13 +116,19 @@
back_frame->pc = frame->pc;
back_frame->sp = frame->sp;
- back_frame->func_name = demangle(frame->function_name.c_str());
+ char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name != nullptr) {
+ back_frame->func_name = demangled_name;
+ free(demangled_name);
+ } else {
+ back_frame->func_name = frame->function_name;
+ }
back_frame->func_offset = frame->function_offset;
back_frame->map.name = frame->map_name;
back_frame->map.start = frame->map_start;
back_frame->map.end = frame->map_end;
- back_frame->map.offset = frame->map_offset;
+ back_frame->map.offset = frame->map_elf_start_offset;
back_frame->map.load_bias = frame->map_load_bias;
back_frame->map.flags = frame->map_flags;
}
@@ -125,22 +136,6 @@
return true;
}
-bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
- const backtrace_stackinfo_t& stack,
- std::vector<backtrace_frame_data_t>* frames,
- BacktraceUnwindError* error) {
- UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
- // Create the process memory from the stack data since this will almost
- // always be different each unwind.
- if (!offline_map->CreateProcessMemory(stack)) {
- if (error != nullptr) {
- error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
- }
- return false;
- }
- return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
-}
-
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
: BacktraceCurrent(pid, tid, map) {}
@@ -167,7 +162,7 @@
}
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
- : BacktracePtrace(pid, tid, map), memory_(pid) {}
+ : BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
return GetMap()->GetFunctionName(pc, offset);
@@ -185,73 +180,5 @@
}
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
- return memory_.Read(addr, buffer, bytes);
-}
-
-UnwindStackOffline::UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map,
- bool map_shared)
- : Backtrace(pid, tid, map), arch_(arch) {
- map_shared_ = map_shared;
-}
-
-bool UnwindStackOffline::Unwind(size_t num_ignore_frames, void* ucontext) {
- if (ucontext == nullptr) {
- return false;
- }
-
- unwindstack::ArchEnum arch;
- switch (arch_) {
- case ARCH_ARM:
- arch = unwindstack::ARCH_ARM;
- break;
- case ARCH_ARM64:
- arch = unwindstack::ARCH_ARM64;
- break;
- case ARCH_X86:
- arch = unwindstack::ARCH_X86;
- break;
- case ARCH_X86_64:
- arch = unwindstack::ARCH_X86_64;
- break;
- default:
- return false;
- }
-
- std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
-
- return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
-}
-
-std::string UnwindStackOffline::GetFunctionNameRaw(uint64_t, uint64_t*) {
- return "";
-}
-
-size_t UnwindStackOffline::Read(uint64_t, uint8_t*, size_t) {
- return 0;
-}
-
-bool UnwindStackOffline::ReadWord(uint64_t, word_t*) {
- return false;
-}
-
-Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
- const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack) {
- std::unique_ptr<UnwindStackOfflineMap> map(
- reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
- if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
- return nullptr;
- }
- return new UnwindStackOffline(arch, pid, tid, map.release(), false);
-}
-
-Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
- if (map == nullptr) {
- return nullptr;
- }
- return new UnwindStackOffline(arch, pid, tid, map, true);
-}
-
-void Backtrace::SetGlobalElfCache(bool enable) {
- unwindstack::Elf::SetCachingEnabled(enable);
+ return memory_->Read(addr, buffer, bytes);
}
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 33c4282..47f6757 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <memory>
#include <string>
#include <backtrace/BacktraceMap.h>
@@ -49,23 +50,7 @@
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
private:
- unwindstack::MemoryRemote memory_;
-};
-
-class UnwindStackOffline : public Backtrace {
- public:
- UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map, bool map_shared);
-
- bool Unwind(size_t num_ignore_frames, void* context) override;
-
- std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset);
-
- size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
-
- bool ReadWord(uint64_t ptr, word_t* out_value) override;
-
- private:
- ArchEnum arch_;
+ std::shared_ptr<unwindstack::Memory> memory_;
};
#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9d15af2..aa0b17c 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -55,7 +55,7 @@
}
// Iterate through the maps and fill in the backtrace_map_t structure.
- for (auto* map_info : *stack_maps_) {
+ for (const auto& map_info : *stack_maps_) {
backtrace_map_t map;
map.start = map_info->start;
map.end = map_info->end;
@@ -132,43 +132,6 @@
return process_memory_;
}
-UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
-
-bool UnwindStackOfflineMap::Build() {
- return false;
-}
-
-bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
- for (const backtrace_map_t& map : backtrace_maps) {
- maps_.push_back(map);
- }
-
- std::sort(maps_.begin(), maps_.end(),
- [](const backtrace_map_t& a, const backtrace_map_t& b) { return a.start < b.start; });
-
- unwindstack::Maps* maps = new unwindstack::Maps;
- stack_maps_.reset(maps);
- for (const backtrace_map_t& map : maps_) {
- maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
- }
- return true;
-}
-
-bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
- if (stack.start >= stack.end) {
- return false;
- }
-
- // Create the process memory from the stack data.
- if (memory_ == nullptr) {
- memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end);
- process_memory_.reset(memory_);
- } else {
- memory_->Reset(stack.data, stack.start, stack.end);
- }
- return true;
-}
-
//-------------------------------------------------------------------------
// BacktraceMap create function.
//-------------------------------------------------------------------------
@@ -189,15 +152,3 @@
}
return map;
}
-
-//-------------------------------------------------------------------------
-// BacktraceMap create offline function.
-//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
- UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
- if (!map->Build(maps)) {
- delete map;
- return nullptr;
- }
- return map;
-}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index e19b605..f0e7d8b 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -33,6 +33,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
// Forward declarations.
class UnwindDexFile;
@@ -74,19 +75,4 @@
unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
};
-class UnwindStackOfflineMap : public UnwindStackMap {
- public:
- UnwindStackOfflineMap(pid_t pid);
- ~UnwindStackOfflineMap() = default;
-
- bool Build() override;
-
- bool Build(const std::vector<backtrace_map_t>& maps);
-
- bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
-
- private:
- unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
-};
-
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
deleted file mode 100644
index 662fb99..0000000
--- a/libbacktrace/backtrace_offline_test.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/threads.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
-
-#include <gtest/gtest.h>
-
-#include "BacktraceTest.h"
-
-struct FunctionSymbol {
- std::string name;
- uint64_t start;
- uint64_t end;
-};
-
-static std::vector<FunctionSymbol> GetFunctionSymbols() {
- std::vector<FunctionSymbol> symbols = {
- {"unknown_start", 0, 0},
- {"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
- {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
- {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
- {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
- {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
- {"test_get_context_and_wait",
- reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
- {"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
- };
- std::sort(
- symbols.begin(), symbols.end(),
- [](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
- for (size_t i = 0; i + 1 < symbols.size(); ++i) {
- symbols[i].end = symbols[i + 1].start;
- }
- return symbols;
-}
-
-static std::string RawDataToHexString(const void* data, size_t size) {
- const uint8_t* p = static_cast<const uint8_t*>(data);
- std::string s;
- for (size_t i = 0; i < size; ++i) {
- s += android::base::StringPrintf("%02x", p[i]);
- }
- return s;
-}
-
-static void HexStringToRawData(const char* s, std::vector<uint8_t>* data, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- int value;
- sscanf(s, "%02x", &value);
- data->push_back(value);
- s += 2;
- }
-}
-
-struct OfflineThreadArg {
- std::vector<uint8_t> ucontext;
- pid_t tid;
- volatile int exit_flag;
-};
-
-static void* OfflineThreadFunc(void* arg) {
- OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
- fn_arg->tid = android::base::GetThreadId();
- BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
- return nullptr;
-}
-
-std::string GetTestPath(const std::string& arch, const std::string& path) {
- return android::base::GetExecutableDirectory() + "/testdata/" + arch + '/' + path;
-}
-
-// This test is disable because it is for generating test data.
-TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
- // Create a thread to generate the needed stack and registers information.
- const size_t stack_size = 16 * 1024;
- void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(MAP_FAILED, stack);
- uint64_t stack_addr = reinterpret_cast<uint64_t>(stack);
- pthread_attr_t attr;
- ASSERT_EQ(0, pthread_attr_init(&attr));
- ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
- pthread_t thread;
- OfflineThreadArg arg;
- arg.exit_flag = 0;
- ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
- // Wait for the offline thread to generate the stack and context information.
- sleep(1);
- // Copy the stack information.
- std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
- reinterpret_cast<uint8_t*>(stack) + stack_size);
- arg.exit_flag = 1;
- ASSERT_EQ(0, pthread_join(thread, nullptr));
- ASSERT_EQ(0, munmap(stack, stack_size));
-
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
- ASSERT_TRUE(map != nullptr);
-
- backtrace_stackinfo_t stack_info;
- stack_info.start = stack_addr;
- stack_info.end = stack_addr + stack_size;
- stack_info.data = stack_data.data();
-
- // Generate offline testdata.
- std::string testdata;
- // 1. Dump pid, tid
- testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
- // 2. Dump maps
- for (auto it = map->begin(); it != map->end(); ++it) {
- const backtrace_map_t* entry = *it;
- testdata +=
- android::base::StringPrintf("map: start: %" PRIx64 " end: %" PRIx64 " offset: %" PRIx64
- " load_bias: %" PRIx64 " flags: %d name: %s\n",
- entry->start, entry->end, entry->offset, entry->load_bias,
- entry->flags, entry->name.c_str());
- }
- // 3. Dump ucontext
- testdata += android::base::StringPrintf("ucontext: %zu ", arg.ucontext.size());
- testdata += RawDataToHexString(arg.ucontext.data(), arg.ucontext.size());
- testdata.push_back('\n');
-
- // 4. Dump stack
- testdata += android::base::StringPrintf(
- "stack: start: %" PRIx64 " end: %" PRIx64 " size: %zu ",
- stack_info.start, stack_info.end, stack_data.size());
- testdata += RawDataToHexString(stack_data.data(), stack_data.size());
- testdata.push_back('\n');
-
- // 5. Dump function symbols
- std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
- for (const auto& symbol : function_symbols) {
- testdata +=
- android::base::StringPrintf("function: start: %" PRIx64 " end: %" PRIx64 " name: %s\n",
- symbol.start, symbol.end, symbol.name.c_str());
- }
-
- ASSERT_TRUE(android::base::WriteStringToFile(testdata, "offline_testdata"));
-}
-
-// Return the name of the function which matches the address. Although we don't know the
-// exact end of each function, it is accurate enough for the tests.
-static std::string FunctionNameForAddress(uint64_t addr,
- const std::vector<FunctionSymbol>& symbols) {
- for (auto& symbol : symbols) {
- if (addr >= symbol.start && addr < symbol.end) {
- return symbol.name;
- }
- }
- return "";
-}
-
-struct OfflineTestData {
- int pid;
- int tid;
- std::vector<backtrace_map_t> maps;
- std::vector<uint8_t> ucontext;
- backtrace_stackinfo_t stack_info;
- std::vector<uint8_t> stack;
- std::vector<FunctionSymbol> symbols;
-};
-
-bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
- std::string s;
- if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
- return false;
- }
- // Parse offline_testdata.
- std::vector<std::string> lines = android::base::Split(s, "\n");
- for (const auto& line : lines) {
- if (android::base::StartsWith(line, "pid:")) {
- sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
- } else if (android::base::StartsWith(line, "map:")) {
- testdata->maps.resize(testdata->maps.size() + 1);
- backtrace_map_t& map = testdata->maps.back();
- int pos;
- sscanf(line.c_str(),
- "map: start: %" SCNx64 " end: %" SCNx64 " offset: %" SCNx64 " load_bias: %" SCNx64
- " flags: %d name: %n",
- &map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
- map.name = android::base::Trim(line.substr(pos));
- } else if (android::base::StartsWith(line, "ucontext:")) {
- size_t size;
- int pos;
- testdata->ucontext.clear();
- sscanf(line.c_str(), "ucontext: %zu %n", &size, &pos);
- HexStringToRawData(&line[pos], &testdata->ucontext, size);
- } else if (android::base::StartsWith(line, "stack:")) {
- size_t size;
- int pos;
- sscanf(line.c_str(),
- "stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
- &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
- CHECK_EQ(testdata->stack_info.end - testdata->stack_info.start, size);
- testdata->stack.clear();
- HexStringToRawData(&line[pos], &testdata->stack, size);
- testdata->stack_info.data = testdata->stack.data();
- } else if (android::base::StartsWith(line, "function:")) {
- testdata->symbols.resize(testdata->symbols.size() + 1);
- FunctionSymbol& symbol = testdata->symbols.back();
- int pos;
- sscanf(line.c_str(), "function: start: %" SCNx64 " end: %" SCNx64 " name: %n", &symbol.start,
- &symbol.end, &pos);
- symbol.name = line.substr(pos);
- }
- }
- return true;
-}
-
-static void BacktraceOfflineTest(std::string arch_str, const std::string& testlib_name) {
- const std::string testlib_path(GetTestPath(arch_str, testlib_name));
- const std::string offline_testdata_path(GetTestPath(arch_str, "offline_testdata"));
- OfflineTestData testdata;
- ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata)) << "Failed " << arch_str;
-
- // Fix path of libbacktrace_testlib.so.
- for (auto& map : testdata.maps) {
- if (map.name.find("libbacktrace_test.so") != std::string::npos) {
- map.name = testlib_path;
- }
- }
-
- Backtrace::ArchEnum arch;
- if (arch_str == "arm") {
- arch = Backtrace::ARCH_ARM;
- } else if (arch_str == "arm64") {
- arch = Backtrace::ARCH_ARM64;
- } else if (arch_str == "x86") {
- arch = Backtrace::ARCH_X86;
- } else if (arch_str == "x86_64") {
- arch = Backtrace::ARCH_X86_64;
- } else {
- abort();
- }
-
- std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
- arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
- ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
-
- ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data())) << "Failed " << arch_str;
-
- // Collect pc values of the call stack frames.
- std::vector<uint64_t> pc_values;
- for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
- pc_values.push_back(backtrace->GetFrame(i)->pc);
- }
-
- size_t test_one_index = 0;
- for (size_t i = 0; i < pc_values.size(); ++i) {
- if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
- test_one_index = i;
- break;
- }
- }
-
- ASSERT_GE(test_one_index, 3u) << "Failed " << arch_str;
- ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_three",
- FunctionNameForAddress(pc_values[test_one_index - 2], testdata.symbols))
- << "Failed " << arch_str;
- ASSERT_EQ("test_level_four",
- FunctionNameForAddress(pc_values[test_one_index - 3], testdata.symbols))
- << "Failed " << arch_str;
-}
-
-// For now, these tests can only run on the given architectures.
-TEST_F(BacktraceTest, offline_eh_frame) {
- BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
- BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
-}
-
-TEST_F(BacktraceTest, offline_debug_frame) {
- BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
- BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
-}
-
-TEST_F(BacktraceTest, offline_gnu_debugdata) {
- BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
- BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
-}
-
-TEST_F(BacktraceTest, offline_arm_exidx) {
- BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
-}
-
-static void LibUnwindingTest(const std::string& arch_str, const std::string& testdata_name,
- const std::string& testlib_name) {
- const std::string testlib_path(GetTestPath(arch_str, testlib_name));
- struct stat st;
- ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
-
- const std::string offline_testdata_path(GetTestPath(arch_str, testdata_name));
- OfflineTestData testdata;
- ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
-
- // Fix path of the testlib.
- for (auto& map : testdata.maps) {
- if (map.name.find(testlib_name) != std::string::npos) {
- map.name = testlib_path;
- }
- }
-
- Backtrace::ArchEnum arch;
- if (arch_str == "arm") {
- arch = Backtrace::ARCH_ARM;
- } else if (arch_str == "arm64") {
- arch = Backtrace::ARCH_ARM64;
- } else if (arch_str == "x86") {
- arch = Backtrace::ARCH_X86;
- } else if (arch_str == "x86_64") {
- arch = Backtrace::ARCH_X86_64;
- } else {
- ASSERT_TRUE(false) << "Unsupported arch " << arch_str;
- abort();
- }
-
- // Do offline backtrace.
- std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
- arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
- ASSERT_TRUE(backtrace != nullptr);
-
- ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data()));
-
- ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
- for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
- std::string name = FunctionNameForAddress(backtrace->GetFrame(i)->rel_pc, testdata.symbols);
- ASSERT_EQ(name, testdata.symbols[i].name);
- }
- ASSERT_TRUE(backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED ||
- backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING ||
- backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_REPEATED_FRAME);
-}
-
-// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
-// overlap with each other, which appears in /system/lib/libart.so.
-TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
- LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
-}
-
-TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
- LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
-}
-
-TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
- LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
-}
-
-TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
- // Make sure we can unwind through functions with CIE entry containing P augmentation, which
- // makes unwinding library reading personality handler from memory. One example is
- // /system/lib64/libskia.so.
- LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
-}
-
-TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
- // Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
- // /vendor/lib64/egl/eglSubDriverAndroid.so.
- LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
-}
-
-TEST_F(BacktraceTest, offline_max_frames_limit) {
- // The length of callchain can reach 256 when recording an application.
- ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
-}
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 10e790b..664b531 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -64,6 +64,8 @@
BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
// Unwind information stopped due to sp/pc repeating.
BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
+ // Unwind information stopped due to invalid elf.
+ BACKTRACE_UNWIND_ERROR_INVALID_ELF,
};
struct BacktraceUnwindError {
@@ -124,24 +126,6 @@
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
- // Create an offline Backtrace object that can be used to do an unwind without a process
- // that is still running. By default, information is only cached in the map
- // file. If the calling code creates the map, data can be cached between
- // unwinds. If not, all cached data will be destroyed when the Backtrace
- // object is destroyed.
- static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
- const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack);
- static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map);
-
- // Create an offline Backtrace object that can be used to do an unwind without a process
- // that is still running. If cache_file is set to true, then elf information will be cached
- // for this call. The cached information survives until the calling process ends. This means
- // that subsequent calls to create offline Backtrace objects will continue to use the same
- // cache. It also assumes that the elf files used for each offline unwind are the same.
- static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
- const backtrace_stackinfo_t& stack, bool cache_file = false);
-
virtual ~Backtrace();
// Get the current stack trace and store in the backtrace_ structure.
@@ -151,11 +135,6 @@
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
- static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
- const backtrace_stackinfo_t& stack_info,
- std::vector<backtrace_frame_data_t>* frames,
- BacktraceUnwindError* error = nullptr);
-
// Get the function name and offset into the function given the pc.
// If the string is empty, then no valid function name was found,
// or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index c564271..f8d5058 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -69,8 +69,6 @@
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
- static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
-
virtual ~BacktraceMap();
class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
new file mode 100644
index 0000000..5aadfe2
--- /dev/null
+++ b/libcrypto_utils/tests/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test_host {
+ name: "libcrypto_utils_test",
+ srcs: ["android_pubkey_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libcrypto_utils",
+ "libcrypto",
+ ],
+}
diff --git a/libcrypto_utils/tests/Android.mk b/libcrypto_utils/tests/Android.mk
deleted file mode 100644
index ef3d0cf..0000000
--- a/libcrypto_utils/tests/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libcrypto_utils_test
-LOCAL_SRC_FILES := android_pubkey_test.cpp
-LOCAL_CFLAGS := -Wall -Werror -Wextra
-LOCAL_SHARED_LIBRARIES := libcrypto_utils libcrypto
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 37afb98..319a73a 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -18,7 +18,6 @@
// they correspond to features not used by our host development tools
// which are also hard or even impossible to port to native Win32
libcutils_nonwindows_sources = [
- "android_get_control_file.cpp",
"fs.cpp",
"hashmap.cpp",
"multiuser.cpp",
@@ -35,6 +34,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
vendor: {
@@ -58,15 +58,14 @@
},
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
srcs: [
"config_utils.cpp",
- "fs_config.cpp",
"canned_fs_config.cpp",
"iosched_policy.cpp",
"load_file.cpp",
"native_handle.cpp",
"record_stream.cpp",
- "sched_policy.cpp",
"sockets.cpp",
"strdup16to8.cpp",
"strdup8to16.cpp",
@@ -81,20 +80,21 @@
not_windows: {
srcs: libcutils_nonwindows_sources + [
"ashmem-host.cpp",
+ "fs_config.cpp",
"trace-host.cpp",
],
},
windows: {
+ host_ldlibs: ["-lws2_32"],
+
srcs: [
"socket_inaddr_any_server_windows.cpp",
"socket_network_client_windows.cpp",
"sockets_windows.cpp",
+ "trace-host.cpp",
],
enabled: true,
- shared: {
- enabled: false,
- },
cflags: [
"-D_GNU_SOURCE",
],
@@ -102,8 +102,10 @@
android: {
srcs: libcutils_nonwindows_sources + [
+ "android_get_control_file.cpp",
"android_reboot.cpp",
"ashmem-dev.cpp",
+ "fs_config.cpp",
"klog.cpp",
"partition_utils.cpp",
"properties.cpp",
@@ -172,13 +174,20 @@
}
},
- shared_libs: ["liblog"],
+ shared_libs: [
+ "liblog",
+ "libbase",
+ ],
header_libs: [
"libbase_headers",
"libcutils_headers",
"libutils_headers",
+ "libprocessgroup_headers",
],
- export_header_lib_headers: ["libcutils_headers"],
+ export_header_lib_headers: [
+ "libcutils_headers",
+ "libprocessgroup_headers",
+ ],
local_include_dirs: ["include"],
cflags: [
@@ -188,4 +197,75 @@
],
}
-subdirs = ["tests"]
+cc_defaults {
+ name: "libcutils_test_default",
+ srcs: ["sockets_test.cpp"],
+
+ target: {
+ android: {
+ srcs: [
+ "android_get_control_file_test.cpp",
+ "android_get_control_socket_test.cpp",
+ "ashmem_test.cpp",
+ "fs_config_test.cpp",
+ "memset_test.cpp",
+ "multiuser_test.cpp",
+ "properties_test.cpp",
+ "sched_policy_test.cpp",
+ "str_parms_test.cpp",
+ "trace-dev_test.cpp",
+ ],
+ },
+
+ not_windows: {
+ srcs: [
+ "str_parms_test.cpp",
+ ],
+ },
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
+
+test_libraries = [
+ "libcutils",
+ "liblog",
+ "libbase",
+ "libjsoncpp",
+ "libprocessgroup",
+ "libcgrouprc",
+]
+
+cc_test {
+ name: "libcutils_test",
+ test_suites: ["device-tests"],
+ defaults: ["libcutils_test_default"],
+ host_supported: true,
+ shared_libs: test_libraries,
+}
+
+cc_test {
+ name: "libcutils_test_static",
+ test_suites: ["device-tests"],
+ defaults: ["libcutils_test_default"],
+ static_libs: [
+ "libc",
+ "libcgrouprc_format",
+ ] + test_libraries,
+ stl: "libc++_static",
+
+ target: {
+ android: {
+ static_executable: true,
+ },
+ windows: {
+ host_ldlibs: ["-lws2_32"],
+
+ enabled: true,
+ },
+ },
+}
diff --git a/libcutils/android_get_control_env.h b/libcutils/android_get_control_env.h
index 638c831..a830269 100644
--- a/libcutils/android_get_control_env.h
+++ b/libcutils/android_get_control_env.h
@@ -14,20 +14,13 @@
* limitations under the License.
*/
-#ifndef __CUTILS_ANDROID_GET_CONTROL_ENV_H
-#define __CUTILS_ANDROID_GET_CONTROL_ENV_H
+#pragma once
-/* To declare library function hidden and internal */
-#define LIBCUTILS_HIDDEN __attribute__((visibility("hidden")))
+#include <sys/cdefs.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
- const char* name);
-#ifdef __cplusplus
-}
-#endif
+int __android_get_control_from_env(const char* prefix, const char* name)
+ __attribute__((visibility("hidden")));
-#endif /* __CUTILS_ANDROID_GET_CONTROL_ENV_H */
+__END_DECLS
diff --git a/libcutils/android_get_control_file.cpp b/libcutils/android_get_control_file.cpp
index d8121f5..d5b0894 100644
--- a/libcutils/android_get_control_file.cpp
+++ b/libcutils/android_get_control_file.cpp
@@ -39,14 +39,14 @@
#include <sys/types.h>
#include <unistd.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
#include "android_get_control_env.h"
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(exp) (exp) // KISS implementation
-#endif
-
-LIBCUTILS_HIDDEN int __android_get_control_from_env(const char* prefix,
- const char* name) {
+int __android_get_control_from_env(const char* prefix, const char* name) {
if (!prefix || !name) return -1;
char *key = NULL;
@@ -67,48 +67,33 @@
long fd = strtol(val, NULL, 10);
if (errno) return -1;
- // validity checking
+ // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
if ((fd < 0) || (fd > INT_MAX)) return -1;
- // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
-
// Still open?
-#if defined(F_GETFD) // Lowest overhead
if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
-#elif defined(F_GETFL) // Alternate lowest overhead
- if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
-#else // Hail Mary pass
- struct stat s;
- if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
-#endif
return static_cast<int>(fd);
}
int android_get_control_file(const char* path) {
- int fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+ std::string given_path;
+ if (!android::base::Realpath(path, &given_path)) return -1;
-#if defined(__linux__)
- // Find file path from /proc and make sure it is correct
- char *proc = NULL;
- if (asprintf(&proc, "/proc/self/fd/%d", fd) < 0) return -1;
- if (!proc) return -1;
-
- size_t len = strlen(path);
- // readlink() does not guarantee a nul byte, len+2 so we catch truncation.
- char *buf = static_cast<char *>(calloc(1, len + 2));
- if (!buf) {
- free(proc);
- return -1;
+ // Try path, then realpath(path), as keys to get the fd from env.
+ auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
+ if (fd < 0) {
+ fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());
+ if (fd < 0) return fd;
}
- ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
- free(proc);
- int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
- free(buf);
- if (ret < 0) return -1;
- if (cmp != 0) return -1;
+
+ // Find file path from /proc and make sure it is correct
+ auto proc = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string fd_path;
+ if (!android::base::Realpath(proc, &fd_path)) return -1;
+
+ if (given_path != fd_path) return -1;
// It is what we think it is
-#endif
return fd;
}
diff --git a/libcutils/tests/android_get_control_file_test.cpp b/libcutils/android_get_control_file_test.cpp
similarity index 97%
rename from libcutils/tests/android_get_control_file_test.cpp
rename to libcutils/android_get_control_file_test.cpp
index 6c6fd2a..8de8530 100644
--- a/libcutils/tests/android_get_control_file_test.cpp
+++ b/libcutils/android_get_control_file_test.cpp
@@ -23,8 +23,8 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <cutils/android_get_control_file.h>
#include <gtest/gtest.h>
diff --git a/libcutils/tests/android_get_control_socket_test.cpp b/libcutils/android_get_control_socket_test.cpp
similarity index 100%
rename from libcutils/tests/android_get_control_socket_test.cpp
rename to libcutils/android_get_control_socket_test.cpp
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
index ce41cd3..e0def71 100644
--- a/libcutils/android_reboot.cpp
+++ b/libcutils/android_reboot.cpp
@@ -23,12 +23,12 @@
#define TAG "android_reboot"
-int android_reboot(int cmd, int /*flags*/, const char* arg) {
+int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
int ret;
const char* restart_cmd = NULL;
char* prop_value;
- switch (static_cast<unsigned>(cmd)) {
+ switch (cmd) {
case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 0cc4fc0..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -23,20 +23,40 @@
*/
#define LOG_TAG "ashmem"
+#ifndef __ANDROID_VNDK__
+#include <dlfcn.h>
+#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/ashmem.h>
+#include <linux/memfd.h>
+#include <log/log.h>
#include <pthread.h>
+#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
-#include <log/log.h>
+
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
#define ASHMEM_DEVICE "/dev/ashmem"
+/* Will be added to UAPI once upstream change is merged */
+#define F_SEAL_FUTURE_WRITE 0x0010
+
+/*
+ * The minimum vendor API level at and after which it is safe to use memfd.
+ * This is to facilitate deprecation of ashmem.
+ */
+#define MIN_MEMFD_VENDOR_API_LEVEL 29
+#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
+
/* ashmem identity */
static dev_t __ashmem_rdev;
/*
@@ -45,13 +65,175 @@
*/
static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
+/*
+ * We use ashmemd to enforce that apps don't open /dev/ashmem directly. Vendor
+ * code can't access system aidl services per Treble requirements. So we limit
+ * ashmemd access to the system variant of libcutils.
+ */
+#ifndef __ANDROID_VNDK__
+using openFdType = int (*)();
+
+static openFdType openFd;
+
+openFdType initOpenAshmemFd() {
+ openFdType openFd = nullptr;
+ void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
+ if (!handle) {
+ ALOGE("Failed to dlopen() libashmemd_client.so: %s", dlerror());
+ return openFd;
+ }
+
+ openFd = reinterpret_cast<openFdType>(dlsym(handle, "openAshmemdFd"));
+ if (!openFd) {
+ ALOGE("Failed to dlsym() openAshmemdFd() function: %s", dlerror());
+ }
+ return openFd;
+}
+#endif
+
+/*
+ * has_memfd_support() determines if the device can use memfd. memfd support
+ * has been there for long time, but certain things in it may be missing. We
+ * check for needed support in it. Also we check if the VNDK version of
+ * libcutils being used is new enough, if its not, then we cannot use memfd
+ * since the older copies may be using ashmem so we just use ashmem. Once all
+ * Android devices that are getting updates are new enough (ex, they were
+ * originally shipped with Android release > P), then we can just use memfd and
+ * delete all ashmem code from libcutils (while preserving the interface).
+ *
+ * NOTE:
+ * The sys.use_memfd property is set by default to false in Android
+ * to temporarily disable memfd, till vendor and apps are ready for it.
+ * The main issue: either apps or vendor processes can directly make ashmem
+ * IOCTLs on FDs they receive by assuming they are ashmem, without going
+ * through libcutils. Such fds could have very well be originally created with
+ * libcutils hence they could be memfd. Thus the IOCTLs will break.
+ *
+ * Set default value of sys.use_memfd property to true once the issue is
+ * resolved, so that the code can then self-detect if kernel support is present
+ * on the device. The property can also set to true from adb shell, for
+ * debugging.
+ */
+
+static bool debug_log = false; /* set to true for verbose logging and other debug */
+static bool pin_deprecation_warn = true; /* Log the pin deprecation warning only once */
+
+/* Determine if vendor processes would be ok with memfd in the system:
+ *
+ * If VNDK is using older libcutils, don't use memfd. This is so that the
+ * same shared memory mechanism is used across binder transactions between
+ * vendor partition processes and system partition processes.
+ */
+static bool check_vendor_memfd_allowed() {
+ std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
+
+ if (vndk_version == "") {
+ ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
+ vndk_version.c_str());
+ return false;
+ }
+
+ /* No issues if vendor is targetting current Dessert */
+ if (vndk_version == "current") {
+ return false;
+ }
+
+ /* Check if VNDK version is a number and act on it */
+ char* p;
+ long int vers = strtol(vndk_version.c_str(), &p, 10);
+ if (*p == 0) {
+ if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
+ ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
+ vndk_version.c_str());
+ return false;
+ }
+
+ return true;
+ }
+
+ /* If its not a number, assume string, but check if its a sane string */
+ if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
+ ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
+ vndk_version.c_str());
+ return false;
+ }
+
+ if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
+ ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
+ vndk_version.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Determine if memfd can be supported. This is just one-time hardwork
+ * which will be cached by the caller.
+ */
+static bool __has_memfd_support() {
+ if (check_vendor_memfd_allowed() == false) {
+ return false;
+ }
+
+ /* Used to turn on/off the detection at runtime, in the future this
+ * property will be removed once we switch everything over to ashmem.
+ * Currently it is used only for debugging to switch the system over.
+ */
+ if (!android::base::GetBoolProperty("sys.use_memfd", false)) {
+ if (debug_log) {
+ ALOGD("sys.use_memfd=false so memfd disabled\n");
+ }
+ return false;
+ }
+
+ /* Check if kernel support exists, otherwise fall back to ashmem */
+ android::base::unique_fd fd(
+ syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
+ if (fd == -1) {
+ ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
+ return false;
+ }
+
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
+ ALOGE("fcntl(F_ADD_SEALS) failed: %s, no memfd support.\n", strerror(errno));
+ return false;
+ }
+
+ if (debug_log) {
+ ALOGD("memfd: device has memfd support, using it\n");
+ }
+ return true;
+}
+
+static bool has_memfd_support() {
+ /* memfd_supported is the initial global per-process state of what is known
+ * about memfd.
+ */
+ static bool memfd_supported = __has_memfd_support();
+
+ return memfd_supported;
+}
+
/* logistics of getting file descriptor for ashmem */
static int __ashmem_open_locked()
{
int ret;
struct stat st;
- int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
+ int fd = -1;
+#ifndef __ANDROID_VNDK__
+ if (!openFd) {
+ openFd = initOpenAshmemFd();
+ }
+
+ if (openFd) {
+ fd = openFd();
+ }
+#endif
+ if (fd < 0) {
+ fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
+ }
if (fd < 0) {
return fd;
}
@@ -141,11 +323,49 @@
return result;
}
+static bool memfd_is_ashmem(int fd) {
+ static bool fd_check_error_once = false;
+
+ if (__ashmem_is_ashmem(fd, 0) == 0) {
+ if (!fd_check_error_once) {
+ ALOGE("memfd: memfd expected but ashmem fd used - please use libcutils.\n");
+ fd_check_error_once = true;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
int ashmem_valid(int fd)
{
+ if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+ return 1;
+ }
+
return __ashmem_is_ashmem(fd, 0) >= 0;
}
+static int memfd_create_region(const char* name, size_t size) {
+ android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
+
+ if (fd == -1) {
+ ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
+ return -1;
+ }
+
+ if (ftruncate(fd, size) == -1) {
+ ALOGE("ftruncate(%s, %zd) failed for memfd creation: %s\n", name, size, strerror(errno));
+ return -1;
+ }
+
+ if (debug_log) {
+ ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
+ }
+ return fd.release();
+}
+
/*
* ashmem_create_region - creates a new ashmem region and returns the file
* descriptor, or <0 on error
@@ -157,6 +377,10 @@
{
int ret, save_errno;
+ if (has_memfd_support()) {
+ return memfd_create_region(name ? name : "none", size);
+ }
+
int fd = __ashmem_open();
if (fd < 0) {
return fd;
@@ -186,28 +410,86 @@
return ret;
}
+static int memfd_set_prot_region(int fd, int prot) {
+ /* Only proceed if an fd needs to be write-protected */
+ if (prot & PROT_WRITE) {
+ return 0;
+ }
+
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
+ ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
int ashmem_set_prot_region(int fd, int prot)
{
+ if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+ return memfd_set_prot_region(fd, prot);
+ }
+
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));
}
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
+ if (!pin_deprecation_warn || debug_log) {
+ ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
+ pin_deprecation_warn = true;
+ }
+
+ if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+ return 0;
+ }
+
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
-
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin)));
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
+ if (!pin_deprecation_warn || debug_log) {
+ ALOGE("Pinning is deprecated since Android Q. Please use trim or other methods.\n");
+ pin_deprecation_warn = true;
+ }
+
+ if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+ return 0;
+ }
+
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
-
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin)));
}
int ashmem_get_size_region(int fd)
{
+ if (has_memfd_support() && !memfd_is_ashmem(fd)) {
+ struct stat sb;
+
+ if (fstat(fd, &sb) == -1) {
+ ALOGE("ashmem_get_size_region(%d): fstat failed: %s\n", fd, strerror(errno));
+ return -1;
+ }
+
+ if (debug_log) {
+ ALOGD("ashmem_get_size_region(%d): %d\n", fd, static_cast<int>(sb.st_size));
+ }
+
+ return sb.st_size;
+ }
+
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
}
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+ pthread_mutex_lock(&__ashmem_lock);
+ openFd = initOpenAshmemFd();
+ pthread_mutex_unlock(&__ashmem_lock);
+#endif //__ANDROID_VNDK__
+}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index bb990d5..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -82,3 +82,5 @@
return buf.st_size;
}
+
+void ashmem_init() {}
diff --git a/libcutils/tests/AshmemTest.cpp b/libcutils/ashmem_test.cpp
similarity index 100%
rename from libcutils/tests/AshmemTest.cpp
rename to libcutils/ashmem_test.cpp
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index bd5f26f..b29638c 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -24,6 +24,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,28 +32,24 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <string>
+
+#include <android-base/strings.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Compat.h>
+#include "fs_config.h"
+
#ifndef O_BINARY
#define O_BINARY 0
#endif
-// My kingdom for <endian.h>
-static inline uint16_t get2LE(const uint8_t* src) {
- return src[0] | (src[1] << 8);
-}
-
-static inline uint64_t get8LE(const uint8_t* src) {
- uint32_t low, high;
-
- low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
- high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
- return ((uint64_t)high << 32) | (uint64_t)low;
-}
+using android::base::EndsWith;
+using android::base::StartsWith;
#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
// Rules for directories.
// These rules are applied based on "first match", so they
@@ -60,7 +57,7 @@
// way up to the root.
static const struct fs_path_config android_dirs[] = {
- // clang-format off
+ // clang-format off
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00555, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
@@ -80,17 +77,18 @@
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
- { 00755, AID_ROOT, AID_SHELL, 0, "product/bin" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "product/bin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "vendor/bin" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor" },
{ 00755, AID_ROOT, AID_ROOT, 0, 0 },
- // clang-format on
+ // clang-format on
};
#ifndef __ANDROID_VNDK__
auto __for_testing_only__android_dirs = android_dirs;
@@ -107,7 +105,9 @@
// although the developer is advised to restrict the scope to the /vendor or
// oem/ file-system since the intent is to provide support for customized
// portions of a separate vendor.img or oem.img. Has to remain open so that
-// customization can also land on /system/vendor, /system/oem or /system/odm.
+// customization can also land on /system/vendor, /system/oem, /system/odm,
+// /system/product or /system/system_ext.
+//
// We expect build-time checking or filtering when constructing the associated
// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
@@ -116,11 +116,14 @@
static const char oem_conf_file[] = "/oem/etc/fs_config_files";
static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
static const char odm_conf_file[] = "/odm/etc/fs_config_files";
+static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
+static const char product_conf_file[] = "/product/etc/fs_config_files";
+static const char system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs";
+static const char system_ext_conf_file[] = "/system_ext/etc/fs_config_files";
static const char* conf[][2] = {
- {sys_conf_file, sys_conf_dir},
- {ven_conf_file, ven_conf_dir},
- {oem_conf_file, oem_conf_dir},
- {odm_conf_file, odm_conf_dir},
+ {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir},
+ {oem_conf_file, oem_conf_dir}, {odm_conf_file, odm_conf_dir},
+ {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir},
};
// Do not use android_files to grant Linux capabilities. Use ambient capabilities in their
@@ -142,15 +145,19 @@
{ 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" },
{ 00600, AID_ROOT, AID_ROOT, 0, "default.prop" }, // legacy
{ 00600, AID_ROOT, AID_ROOT, 0, "system/etc/prop.default" },
- { 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
+ { 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" }, // legacy; only for P release
+ { 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" }, // legacy; only for P release
+ { 00600, AID_ROOT, AID_ROOT, 0, "odm/etc/build.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
{ 00600, AID_ROOT, AID_ROOT, 0, "product/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "product_services/build.prop" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
+ { 00444, AID_ROOT, AID_ROOT, 0, product_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, product_conf_file + 1 },
+ { 00600, AID_ROOT, AID_ROOT, 0, "system_ext/build.prop" },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_file + 1 },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
@@ -184,6 +191,9 @@
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
+ { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
+ CAP_MASK_LONG(CAP_SETGID),
+ "system/bin/simpleperf_app_runner" },
// Support FIFO scheduling mode in SurfaceFlinger.
{ 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE),
@@ -192,12 +202,11 @@
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
{ 00750, AID_ROOT, AID_SHELL, 0, "init*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "odm/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "product/bin/*" },
- { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
- { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/apex/*/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
@@ -235,48 +244,56 @@
return fd;
}
-// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>" or
-// "vendor/<stuff>"
-static bool is_partition(const char* path, size_t len) {
- static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
+// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
+// "system_ext/<stuff>" or "vendor/<stuff>"
+static bool is_partition(const std::string& path) {
+ static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"};
for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
- size_t plen = strlen(partitions[i]);
- if (len <= plen) continue;
- if (!strncmp(path, partitions[i], plen)) return true;
+ if (StartsWith(path, partitions[i])) return true;
}
return false;
}
-static inline bool prefix_cmp(bool partial, const char* prefix, size_t len, const char* path,
- size_t plen) {
- return ((partial && plen >= len) || (plen == len)) && !strncmp(prefix, path, len);
-}
-
// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
// "system/<partition>/<stuff>" to "<partition>/<stuff>"
-static bool fs_config_cmp(bool partial, const char* prefix, size_t len, const char* path,
- size_t plen) {
- // If name ends in * then allow partial matches.
- if (!partial && prefix[len - 1] == '*') {
- len--;
- partial = true;
+static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
+ std::string pattern(prefix, len);
+ std::string input(path, plen);
+
+ // Massage pattern and input so that they can be used by fnmatch where
+ // directories have to end with /.
+ if (dir) {
+ if (!EndsWith(input, "/")) {
+ input.append("/");
+ }
+
+ if (!EndsWith(pattern, "/*")) {
+ if (EndsWith(pattern, "/")) {
+ pattern.append("*");
+ } else {
+ pattern.append("/*");
+ }
+ }
}
- if (prefix_cmp(partial, prefix, len, path, plen)) return true;
+ // no FNM_PATHNAME is set in order to match a/b/c/d with a/*
+ // FNM_ESCAPE is set in order to prevent using \\? and \\* and maintenance issues.
+ const int fnm_flags = FNM_NOESCAPE;
+ if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
- static const char system[] = "system/";
- if (!strncmp(path, system, strlen(system))) {
- path += strlen(system);
- plen -= strlen(system);
- } else if (len <= strlen(system)) {
- return false;
- } else if (strncmp(prefix, system, strlen(system))) {
- return false;
- } else {
- prefix += strlen(system);
- len -= strlen(system);
+ // Check match between logical partition's files and patterns.
+ static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/",
+ "system/vendor/", "vendor/odm/"};
+ for (auto& logical_partition : kLogicalPartitions) {
+ if (StartsWith(input, logical_partition)) {
+ std::string input_in_partition = input.substr(input.find('/') + 1);
+ if (!is_partition(input_in_partition)) continue;
+ if (fnmatch(pattern.c_str(), input_in_partition.c_str(), fnm_flags) == 0) {
+ return true;
+ }
+ }
}
- return is_partition(prefix, len) && prefix_cmp(partial, prefix, len, path, plen);
+ return false;
}
#ifndef __ANDROID_VNDK__
auto __for_testing_only__fs_config_cmp = fs_config_cmp;
@@ -301,7 +318,7 @@
while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
char* prefix;
- uint16_t host_len = get2LE((const uint8_t*)&header.len);
+ uint16_t host_len = header.len;
ssize_t len, remainder = host_len - sizeof(header);
if (remainder <= 0) {
ALOGE("%s len is corrupted", conf[which][dir]);
@@ -326,10 +343,10 @@
if (fs_config_cmp(dir, prefix, len, path, plen)) {
free(prefix);
close(fd);
- *uid = get2LE((const uint8_t*)&(header.uid));
- *gid = get2LE((const uint8_t*)&(header.gid));
- *mode = (*mode & (~07777)) | get2LE((const uint8_t*)&(header.mode));
- *capabilities = get8LE((const uint8_t*)&(header.capabilities));
+ *uid = header.uid;
+ *gid = header.gid;
+ *mode = (*mode & (~07777)) | header.mode;
+ *capabilities = header.capabilities;
return;
}
free(prefix);
@@ -347,21 +364,3 @@
*mode = (*mode & (~07777)) | pc->mode;
*capabilities = pc->capabilities;
}
-
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc) {
- struct fs_path_config_from_file* p = (struct fs_path_config_from_file*)buffer;
- size_t len = ALIGN(sizeof(*p) + strlen(pc->prefix) + 1, sizeof(uint64_t));
-
- if ((length < len) || (len > UINT16_MAX)) {
- return -ENOSPC;
- }
- memset(p, 0, len);
- uint16_t host_len = len;
- p->len = get2LE((const uint8_t*)&host_len);
- p->mode = get2LE((const uint8_t*)&(pc->mode));
- p->uid = get2LE((const uint8_t*)&(pc->uid));
- p->gid = get2LE((const uint8_t*)&(pc->gid));
- p->capabilities = get8LE((const uint8_t*)&(pc->capabilities));
- strcpy(p->prefix, pc->prefix);
- return len;
-}
diff --git a/libcutils/fs_config.h b/libcutils/fs_config.h
new file mode 100644
index 0000000..66ad48b
--- /dev/null
+++ b/libcutils/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// Binary format for the runtime <partition>/etc/fs_config_(dirs|files) filesystem override files.
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/fs_config_test.cpp
similarity index 91%
rename from libcutils/tests/fs_config.cpp
rename to libcutils/fs_config_test.cpp
index d5dc66a..9627152 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/fs_config_test.cpp
@@ -25,7 +25,8 @@
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
-#include <private/fs_config.h>
+
+#include "fs_config.h"
extern const fs_path_config* __for_testing_only__android_dirs;
extern const fs_path_config* __for_testing_only__android_files;
@@ -42,11 +43,15 @@
const char* path;
bool match;
} fs_config_cmp_tests[] = {
- // clang-format off
+ // clang-format off
{ true, "system/lib", "system/lib/hw", true },
{ true, "vendor/lib", "system/vendor/lib/hw", true },
{ true, "system/vendor/lib", "vendor/lib/hw", true },
{ true, "system/vendor/lib", "system/vendor/lib/hw", true },
+ { true, "foo/*/bar/*", "foo/1/bar/2", true },
+ { true, "foo/*/bar/*", "foo/1/bar", true },
+ { true, "foo/*/bar/*", "foo/1/bar/2/3", true },
+ { true, "foo/*/bar/*", "foo/1/bar/2/3/", true },
{ false, "vendor/bin/wifi", "system/vendor/bin/w", false },
{ false, "vendor/bin/wifi", "system/vendor/bin/wifi", true },
{ false, "vendor/bin/wifi", "system/vendor/bin/wifi2", false },
@@ -58,8 +63,14 @@
{ false, "vendor/bin/*", "system/vendor/bin/wifi", true },
{ false, "system/bin/*", "system/bin", false },
{ false, "system/vendor/bin/*", "vendor/bin/wifi", true },
+ { false, "foo/*/bar/*", "foo/1/bar/2", true },
+ { false, "foo/*/bar/*", "foo/1/bar", false },
+ { false, "foo/*/bar/*", "foo/1/bar/2/3", true },
+ { false, "foo/*/bar/*.so", "foo/1/bar/2/3", false },
+ { false, "foo/*/bar/*.so", "foo/1/bar/2.so", true },
+ { false, "foo/*/bar/*.so", "foo/1/bar/2/3.so", true },
{ false, NULL, NULL, false },
- // clang-format on
+ // clang-format on
};
static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 99030ed..cd27eef 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CUTILS_ANDROID_REBOOT_H__
-#define __CUTILS_ANDROID_REBOOT_H__
+#pragma once
#include <sys/cdefs.h>
@@ -36,10 +35,8 @@
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
* Due to that, process calling this should have proper selinux permission
- * to write to the property. Otherwise, the call will fail.
+ * to write to the property or the call will fail.
*/
-int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot(unsigned cmd, int flags, const char* arg);
__END_DECLS
-
-#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
int ashmem_pin_region(int fd, size_t offset, size_t len);
int ashmem_unpin_region(int fd, size_t offset, size_t len);
int ashmem_get_size_region(int fd);
+void ashmem_init();
#ifdef __cplusplus
}
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 10f5bc0..f6cae36 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -23,6 +23,9 @@
extern "C" {
#endif
+#define NATIVE_HANDLE_MAX_FDS 1024
+#define NATIVE_HANDLE_MAX_INTS 1024
+
/* Declare a char array for use with native_handle_init */
#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \
alignas(native_handle_t) char (name)[ \
diff --git a/libcutils/include/cutils/partition_utils.h b/libcutils/include/cutils/partition_utils.h
index 7518559..8bc9b48 100644
--- a/libcutils/include/cutils/partition_utils.h
+++ b/libcutils/include/cutils/partition_utils.h
@@ -21,7 +21,7 @@
__BEGIN_DECLS
-int partition_wiped(char *source);
+int partition_wiped(const char* source);
__END_DECLS
diff --git a/libcutils/include/cutils/sched_policy.h b/libcutils/include/cutils/sched_policy.h
index cf91b76..538ff6b 100644
--- a/libcutils/include/cutils/sched_policy.h
+++ b/libcutils/include/cutils/sched_policy.h
@@ -17,67 +17,10 @@
#ifndef __CUTILS_SCHED_POLICY_H
#define __CUTILS_SCHED_POLICY_H
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/*
- * Check if Linux kernel enables CPUSETS feature.
- *
- * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ * For backwards compatibility only
+ * New users should include processgroup/sched_policy.h directly
*/
-extern bool cpusets_enabled();
-
-/*
- * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
- * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
- *
- * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
- */
-extern bool schedboost_enabled();
-
-/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
-typedef enum {
- SP_DEFAULT = -1,
- SP_BACKGROUND = 0,
- SP_FOREGROUND = 1,
- SP_SYSTEM = 2, // can't be used with set_sched_policy()
- SP_AUDIO_APP = 3,
- SP_AUDIO_SYS = 4,
- SP_TOP_APP = 5,
- SP_RT_APP = 6,
- SP_RESTRICTED = 7,
- SP_CNT,
- SP_MAX = SP_CNT - 1,
- SP_SYSTEM_DEFAULT = SP_FOREGROUND,
-} SchedPolicy;
-
-extern int set_cpuset_policy(int tid, SchedPolicy policy);
-
-/* Assign thread tid to the cgroup associated with the specified policy.
- * If the thread is a thread group leader, that is it's gettid() == getpid(),
- * then the other threads in the same thread group are _not_ affected.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -errno for error.
- */
-extern int set_sched_policy(int tid, SchedPolicy policy);
-
-/* Return the policy associated with the cgroup of thread tid via policy pointer.
- * On platforms which support gettid(), zero tid means current thread.
- * Return value: 0 for success, or -1 for error and set errno.
- */
-extern int get_sched_policy(int tid, SchedPolicy *policy);
-
-/* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
- * the caller is responsible for displaying the useful part of the string.
- */
-extern const char *get_sched_policy_name(SchedPolicy policy);
-
-#ifdef __cplusplus
-}
-#endif
+#include <processgroup/sched_policy.h>
#endif /* __CUTILS_SCHED_POLICY_H */
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 58b9f09..79b4b35 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_VIBRATOR (1<<23)
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
-#define ATRACE_TAG_LAST ATRACE_TAG_NNAPI
+#define ATRACE_TAG_RRO (1<<26)
+#define ATRACE_TAG_LAST ATRACE_TAG_RRO
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 845c586..ae9dab5 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -28,17 +28,10 @@
* mediadrm
* Whose friendly names do not match the #define statements.
*
- * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
- * can be used to define reserved OEM ranges used for sanity checks
- * during the build process. The rules are, they must end with START/END
- * The proper convention is incrementing a number like so:
- * AID_OEM_RESERVED_START
- * AID_OEM_RESERVED_1_START
- * AID_OEM_RESERVED_2_START
- * ...
- * The same applies to the END.
- * They are not required to be in order, but must not overlap each other and
- * must define a START and END'ing range. START must be smaller than END.
+ * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs. 3rd
+ * party AIDs must be added via config.fs, which will place them in the corresponding partition's
+ * passwd and group files. There are ranges in this file reserved for AIDs for each 3rd party
+ * partition, from which the system reads passwd and group files.
*/
#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
@@ -132,14 +125,21 @@
#define AID_LMKD 1069 /* low memory killer daemon */
#define AID_LLKD 1070 /* live lock daemon */
#define AID_IORAPD 1071 /* input/output readahead and pin daemon */
+#define AID_GPU_SERVICE 1072 /* GPU service daemon */
+#define AID_NETWORK_STACK 1073 /* network stack service */
+#define AID_GSID 1074 /* GSI service daemon */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
#define AID_DIAG 2002 /* access to diagnostic resources */
-/* The range 2900-2999 is reserved for OEM, and must never be
- * used here */
+/* The range 2900-2999 is reserved for the vendor partition */
+/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'
+ * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#
+ * created automatically for all values in these ranges. If there is a user/group in a passwd/group
+ * file corresponding to this range, both the oem_# and user/group names will resolve to the same
+ * value. */
#define AID_OEM_RESERVED_START 2900
#define AID_OEM_RESERVED_END 2999
@@ -156,10 +156,26 @@
#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
#define AID_UHID 3011 /* Allow read/write to /dev/uhid node */
-/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+/* The range 5000-5999 is also reserved for vendor partition. */
#define AID_OEM_RESERVED_2_START 5000
#define AID_OEM_RESERVED_2_END 5999
+/* The range 6000-6499 is reserved for the system partition. */
+#define AID_SYSTEM_RESERVED_START 6000
+#define AID_SYSTEM_RESERVED_END 6499
+
+/* The range 6500-6999 is reserved for the odm partition. */
+#define AID_ODM_RESERVED_START 6500
+#define AID_ODM_RESERVED_END 6999
+
+/* The range 7000-7499 is reserved for the product partition. */
+#define AID_PRODUCT_RESERVED_START 7000
+#define AID_PRODUCT_RESERVED_END 7499
+
+/* The range 7500-7999 is reserved for the system_ext partition. */
+#define AID_SYSTEM_EXT_RESERVED_START 7500
+#define AID_SYSTEM_EXT_RESERVED_END 7999
+
#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999
@@ -188,7 +204,8 @@
*/
#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+/* use the ranges below to determine whether a process is isolated */
+#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8926491..8a9a1ff 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -19,44 +19,17 @@
** by the device side of adb.
*/
-#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
-#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
-#include <sys/types.h>
#if defined(__BIONIC__)
#include <linux/capability.h>
#else // defined(__BIONIC__)
-#include "android_filesystem_capability.h"
+#include <private/android_filesystem_capability.h>
#endif // defined(__BIONIC__)
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
-/*
- * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
- * filesystem override files.
- */
-
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
- uint16_t len;
- uint16_t mode;
- uint16_t uid;
- uint16_t gid;
- uint64_t capabilities;
- char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char* prefix;
-};
-
/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
__BEGIN_DECLS
@@ -74,8 +47,4 @@
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
unsigned* mode, uint64_t* capabilities);
-ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
-
__END_DECLS
-
-#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/memset_test.cpp
similarity index 100%
rename from libcutils/tests/MemsetTest.cpp
rename to libcutils/memset_test.cpp
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/multiuser_test.cpp
similarity index 100%
rename from libcutils/tests/multiuser_test.cpp
rename to libcutils/multiuser_test.cpp
diff --git a/libcutils/native_handle.cpp b/libcutils/native_handle.cpp
index 66f7a3d..b409e5b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,9 +22,6 @@
#include <string.h>
#include <unistd.h>
-static const int kMaxNativeFds = 1024;
-static const int kMaxNativeInts = 1024;
-
native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
if ((uintptr_t) storage % alignof(native_handle_t)) {
errno = EINVAL;
@@ -39,7 +36,8 @@
}
native_handle_t* native_handle_create(int numFds, int numInts) {
- if (numFds < 0 || numInts < 0 || numFds > kMaxNativeFds || numInts > kMaxNativeInts) {
+ if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
+ numInts > NATIVE_HANDLE_MAX_INTS) {
errno = EINVAL;
return NULL;
}
diff --git a/libcutils/partition_utils.cpp b/libcutils/partition_utils.cpp
index 2211ff6..b840559 100644
--- a/libcutils/partition_utils.cpp
+++ b/libcutils/partition_utils.cpp
@@ -39,8 +39,7 @@
return ret;
}
-int partition_wiped(char *source)
-{
+int partition_wiped(const char* source) {
uint8_t buf[4096];
int fd, ret;
@@ -67,4 +66,3 @@
return 0;
}
-
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/properties_test.cpp
similarity index 100%
rename from libcutils/tests/PropertiesTest.cpp
rename to libcutils/properties_test.cpp
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
deleted file mode 100644
index 3fa548f..0000000
--- a/libcutils/sched_policy.cpp
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <cutils/sched_policy.h>
-
-#define LOG_TAG "SchedPolicy"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android-base/macros.h>
-#include <log/log.h>
-
-/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
- * Call this any place a SchedPolicy is used as an input parameter.
- * Returns the possibly re-mapped policy.
- */
-static inline SchedPolicy _policy(SchedPolicy p)
-{
- return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
-}
-
-#if defined(__ANDROID__)
-
-#include <pthread.h>
-#include <sched.h>
-#include <sys/prctl.h>
-
-#define POLICY_DEBUG 0
-
-// timer slack value in nS enforced when the thread moves to background
-#define TIMER_SLACK_BG 40000000
-#define TIMER_SLACK_FG 50000
-
-static pthread_once_t the_once = PTHREAD_ONCE_INIT;
-
-static int __sys_supports_timerslack = -1;
-
-// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
-static int system_bg_cpuset_fd = -1;
-static int bg_cpuset_fd = -1;
-static int fg_cpuset_fd = -1;
-static int ta_cpuset_fd = -1; // special cpuset for top app
-static int rs_cpuset_fd = -1; // special cpuset for screen off restrictions
-
-// File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
-static int bg_schedboost_fd = -1;
-static int fg_schedboost_fd = -1;
-static int ta_schedboost_fd = -1;
-static int rt_schedboost_fd = -1;
-
-/* Add tid to the scheduling group defined by the policy */
-static int add_tid_to_cgroup(int tid, int fd)
-{
- if (fd < 0) {
- SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
- errno = EINVAL;
- return -1;
- }
-
- // specialized itoa -- works for tid > 0
- char text[22];
- char *end = text + sizeof(text) - 1;
- char *ptr = end;
- *ptr = '\0';
- while (tid > 0) {
- *--ptr = '0' + (tid % 10);
- tid = tid / 10;
- }
-
- if (write(fd, ptr, end - ptr) < 0) {
- /*
- * If the thread is in the process of exiting,
- * don't flag an error
- */
- if (errno == ESRCH)
- return 0;
- SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
- ptr, strerror(errno), fd);
- errno = EINVAL;
- return -1;
- }
-
- return 0;
-}
-
-/*
- If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
- /dev/cpuset mounted in init.rc; otherwise, that file does not exist
- even though the directory, /dev/cpuset, is still created (by init.rc).
-
- A couple of other candidates (under cpuset mount directory):
- notify_on_release
- release_agent
-
- Yet another way to decide if cpuset is enabled is to parse
- /proc/self/status and search for lines begin with "Mems_allowed".
-
- If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
- be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
- on where init.rc mounts cpuset. That's why we'd better require this
- configuration be set if CONFIG_CPUSETS is set.
-
- In older releases, this was controlled by build-time configuration.
- */
-bool cpusets_enabled() {
- static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
-
- return enabled;
-}
-
-/*
- Similar to CONFIG_CPUSETS above, but with a different configuration
- CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro
- Stable Kernel (LSK), but not in mainline Linux as of v4.9.
-
- In older releases, this was controlled by build-time configuration.
- */
-bool schedboost_enabled() {
- static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
-
- return enabled;
-}
-
-static void __initialize() {
- const char* filename;
-
- if (cpusets_enabled()) {
- if (!access("/dev/cpuset/tasks", W_OK)) {
-
- filename = "/dev/cpuset/foreground/tasks";
- fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/background/tasks";
- bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/system-background/tasks";
- system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/top-app/tasks";
- ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/cpuset/restricted/tasks";
- rs_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
-
- if (schedboost_enabled()) {
- filename = "/dev/stune/top-app/tasks";
- ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/stune/foreground/tasks";
- fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/stune/background/tasks";
- bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- filename = "/dev/stune/rt/tasks";
- rt_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
- }
- }
- }
-
- char buf[64];
- snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
- __sys_supports_timerslack = !access(buf, W_OK);
-}
-
-/*
- * Returns the path under the requested cgroup subsystem (if it exists)
- *
- * The data from /proc/<pid>/cgroup looks (something) like:
- * 2:cpu:/bg_non_interactive
- * 1:cpuacct:/
- *
- * We return the part after the "/", which will be an empty string for
- * the default cgroup. If the string is longer than "bufLen", the string
- * will be truncated.
- */
-static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen)
-{
-#if defined(__ANDROID__)
- char pathBuf[32];
- char lineBuf[256];
- FILE *fp;
-
- snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
- if (!(fp = fopen(pathBuf, "re"))) {
- return -1;
- }
-
- while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
- char *next = lineBuf;
- char *found_subsys;
- char *grp;
- size_t len;
-
- /* Junk the first field */
- if (!strsep(&next, ":")) {
- goto out_bad_data;
- }
-
- if (!(found_subsys = strsep(&next, ":"))) {
- goto out_bad_data;
- }
-
- if (strcmp(found_subsys, subsys)) {
- /* Not the subsys we're looking for */
- continue;
- }
-
- if (!(grp = strsep(&next, ":"))) {
- goto out_bad_data;
- }
- grp++; /* Drop the leading '/' */
- len = strlen(grp);
- grp[len-1] = '\0'; /* Drop the trailing '\n' */
-
- if (bufLen <= len) {
- len = bufLen - 1;
- }
- strncpy(buf, grp, len);
- buf[len] = '\0';
- fclose(fp);
- return 0;
- }
-
- SLOGE("Failed to find subsys %s", subsys);
- fclose(fp);
- return -1;
- out_bad_data:
- SLOGE("Bad cgroup data {%s}", lineBuf);
- fclose(fp);
- return -1;
-#else
- errno = ENOSYS;
- return -1;
-#endif
-}
-
-int get_sched_policy(int tid, SchedPolicy *policy)
-{
- if (tid == 0) {
- tid = gettid();
- }
- pthread_once(&the_once, __initialize);
-
- char grpBuf[32];
-
- grpBuf[0] = '\0';
- if (schedboost_enabled()) {
- if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
- }
- if ((grpBuf[0] == '\0') && cpusets_enabled()) {
- if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
- }
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "system-background")) {
- *policy = SP_SYSTEM;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- return 0;
-}
-
-int set_cpuset_policy(int tid, SchedPolicy policy)
-{
- // in the absence of cpusets, use the old sched policy
- if (!cpusets_enabled()) {
- return set_sched_policy(tid, policy);
- }
-
- if (tid == 0) {
- tid = gettid();
- }
- policy = _policy(policy);
- pthread_once(&the_once, __initialize);
-
- int fd = -1;
- int boost_fd = -1;
- switch (policy) {
- case SP_BACKGROUND:
- fd = bg_cpuset_fd;
- boost_fd = bg_schedboost_fd;
- break;
- case SP_FOREGROUND:
- case SP_AUDIO_APP:
- case SP_AUDIO_SYS:
- fd = fg_cpuset_fd;
- boost_fd = fg_schedboost_fd;
- break;
- case SP_TOP_APP :
- fd = ta_cpuset_fd;
- boost_fd = ta_schedboost_fd;
- break;
- case SP_SYSTEM:
- fd = system_bg_cpuset_fd;
- break;
- case SP_RESTRICTED:
- fd = rs_cpuset_fd;
- break;
- default:
- boost_fd = fd = -1;
- break;
- }
-
- if (add_tid_to_cgroup(tid, fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
- }
-
- if (schedboost_enabled()) {
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
- }
- }
-
- return 0;
-}
-
-static void set_timerslack_ns(int tid, unsigned long slack) {
- // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
- // TODO: once we've backported this, log if the open(2) fails.
- if (__sys_supports_timerslack) {
- char buf[64];
- snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
- int fd = open(buf, O_WRONLY | O_CLOEXEC);
- if (fd != -1) {
- int len = snprintf(buf, sizeof(buf), "%lu", slack);
- if (write(fd, buf, len) != len) {
- SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
- }
- close(fd);
- return;
- }
- }
-
- // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
- if ((tid == 0) || (tid == gettid())) {
- if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
- SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
- }
- }
-}
-
-int set_sched_policy(int tid, SchedPolicy policy)
-{
- if (tid == 0) {
- tid = gettid();
- }
- policy = _policy(policy);
- pthread_once(&the_once, __initialize);
-
-#if POLICY_DEBUG
- char statfile[64];
- char statline[1024];
- char thread_name[255];
-
- snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
- memset(thread_name, 0, sizeof(thread_name));
-
- int fd = open(statfile, O_RDONLY | O_CLOEXEC);
- if (fd >= 0) {
- int rc = read(fd, statline, 1023);
- close(fd);
- statline[rc] = 0;
- char *p = statline;
- char *q;
-
- for (p = statline; *p != '('; p++);
- p++;
- for (q = p; *q != ')'; q++);
-
- strncpy(thread_name, p, (q-p));
- }
- switch (policy) {
- case SP_BACKGROUND:
- SLOGD("vvv tid %d (%s)", tid, thread_name);
- break;
- case SP_FOREGROUND:
- case SP_AUDIO_APP:
- case SP_AUDIO_SYS:
- case SP_TOP_APP:
- SLOGD("^^^ tid %d (%s)", tid, thread_name);
- break;
- case SP_SYSTEM:
- SLOGD("/// tid %d (%s)", tid, thread_name);
- break;
- case SP_RT_APP:
- SLOGD("RT tid %d (%s)", tid, thread_name);
- break;
- default:
- SLOGD("??? tid %d (%s)", tid, thread_name);
- break;
- }
-#endif
-
- if (schedboost_enabled()) {
- int boost_fd = -1;
- switch (policy) {
- case SP_BACKGROUND:
- boost_fd = bg_schedboost_fd;
- break;
- case SP_FOREGROUND:
- case SP_AUDIO_APP:
- case SP_AUDIO_SYS:
- boost_fd = fg_schedboost_fd;
- break;
- case SP_TOP_APP:
- boost_fd = ta_schedboost_fd;
- break;
- case SP_RT_APP:
- boost_fd = rt_schedboost_fd;
- break;
- default:
- boost_fd = -1;
- break;
- }
-
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
- }
-
- }
-
- set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
-
- return 0;
-}
-
-#else
-
-/* Stubs for non-Android targets. */
-
-int set_sched_policy(int /*tid*/, SchedPolicy /*policy*/) {
- return 0;
-}
-
-int get_sched_policy(int /*tid*/, SchedPolicy* policy) {
- *policy = SP_SYSTEM_DEFAULT;
- return 0;
-}
-
-#endif
-
-const char* get_sched_policy_name(SchedPolicy policy) {
- policy = _policy(policy);
- static const char* const kSchedPolicyNames[] = {
- [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ",
- [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta",
- [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs",
- };
- static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
- if (policy < SP_BACKGROUND || policy >= SP_CNT) {
- return "error";
- }
- return kSchedPolicyNames[policy];
-}
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
similarity index 96%
rename from libcutils/tests/sched_policy_test.cpp
rename to libcutils/sched_policy_test.cpp
index 1f657e2..a321c90 100644
--- a/libcutils/tests/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -90,17 +90,18 @@
// A measureable effect of scheduling policy is that the kernel has 800x
// greater slack time in waking up a sleeping background thread.
//
- // Look for 100x difference in how long FB and BG threads actually sleep
+ // Look for 10x difference in how long FB and BG threads actually sleep
// when trying to sleep for 1 ns. This difference is large enough not
// to happen by chance, but small enough (compared to 800x) to keep inherent
// fuzziness in scheduler behavior from causing false negatives.
- const unsigned int BG_FG_SLACK_FACTOR = 100;
+ const unsigned int BG_FG_SLACK_FACTOR = 10;
ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
auto bgSleepTime = medianSleepTime();
ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
auto fgSleepTime = medianSleepTime();
+
ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/sockets_test.cpp
similarity index 100%
rename from libcutils/tests/sockets_test.cpp
rename to libcutils/sockets_test.cpp
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 2248817..6acdcd8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -16,8 +16,6 @@
#include <cutils/sockets.h>
-#define LOG_TAG "socket-unix"
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,9 +25,6 @@
#include <time.h>
#include <unistd.h>
-#include <cutils/android_get_control_file.h>
-#include <log/log.h>
-
#include "android_get_control_env.h"
int socket_close(int sock) {
@@ -62,6 +57,7 @@
return writev(sock, iovec_buffers, num_buffers);
}
+#if defined(__ANDROID__)
int android_get_control_socket(const char* name) {
int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);
@@ -82,3 +78,8 @@
}
return -1;
}
+#else
+int android_get_control_socket(const char*) {
+ return -1;
+}
+#endif
diff --git a/libcutils/tests/test_str_parms.cpp b/libcutils/str_parms_test.cpp
similarity index 100%
rename from libcutils/tests/test_str_parms.cpp
rename to libcutils/str_parms_test.cpp
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
deleted file mode 100644
index 7884190..0000000
--- a/libcutils/tests/Android.bp
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_defaults {
- name: "libcutils_test_default",
- srcs: ["sockets_test.cpp"],
-
- target: {
- android: {
- srcs: [
- "AshmemTest.cpp",
- "MemsetTest.cpp",
- "PropertiesTest.cpp",
- "sched_policy_test.cpp",
- "trace-dev_test.cpp",
- "test_str_parms.cpp",
- "android_get_control_socket_test.cpp",
- "android_get_control_file_test.cpp",
- "multiuser_test.cpp",
- "fs_config.cpp",
- ],
- },
-
- not_windows: {
- srcs: [
- "test_str_parms.cpp",
- ],
- },
- },
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-}
-
-test_libraries = [
- "libcutils",
- "liblog",
- "libbase",
-]
-
-cc_test {
- name: "libcutils_test",
- test_suites: ["device-tests"],
- defaults: ["libcutils_test_default"],
- host_supported: true,
- shared_libs: test_libraries,
-}
-
-cc_test {
- name: "libcutils_test_static",
- test_suites: ["device-tests"],
- defaults: ["libcutils_test_default"],
- static_libs: ["libc"] + test_libraries,
- stl: "libc++_static",
-
- target: {
- android: {
- static_executable: true,
- },
- windows: {
- host_ldlibs: ["-lws2_32"],
-
- enabled: true,
- },
- },
-}
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
deleted file mode 100644
index dd7aca2..0000000
--- a/libcutils/tests/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Config for libcutils_test">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libcutils_test" />
- </test>
-</configuration>
\ No newline at end of file
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index c9580af..e3da77b 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -80,7 +80,7 @@
// Determine whether application-level tracing is enabled for this process.
static bool atrace_is_app_tracing_enabled()
{
- bool sys_debuggable = __android_log_is_debuggable();
+ bool sys_debuggable = property_get_bool("ro.debuggable", 0);
bool result = false;
if (sys_debuggable || atrace_is_debuggable) {
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
similarity index 99%
rename from libcutils/tests/trace-dev_test.cpp
rename to libcutils/trace-dev_test.cpp
index f8d4f00..832b36a 100644
--- a/libcutils/tests/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -22,7 +22,6 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "../trace-dev.cpp"
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index 2dfceed..721de7c 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -95,6 +95,8 @@
int uevent_open_socket(int buf_sz, bool passcred) {
struct sockaddr_nl addr;
int on = passcred;
+ int buf_sz_readback = 0;
+ socklen_t optlen = sizeof(buf_sz_readback);
int s;
memset(&addr, 0, sizeof(addr));
@@ -105,11 +107,21 @@
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
if (s < 0) return -1;
- /* buf_sz should be less than net.core.rmem_max for this to succeed */
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
+ getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
close(s);
return -1;
}
+ /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
+ * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
+ * case we don't have CAP_NET_ADMIN. This is the case, for example, for
+ * healthd. */
+ if (buf_sz_readback < 2 * buf_sz) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
+ close(s);
+ return -1;
+ }
+ }
setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
diff --git a/libion/OWNERS b/libion/OWNERS
new file mode 100644
index 0000000..143ad2d
--- /dev/null
+++ b/libion/OWNERS
@@ -0,0 +1,2 @@
+sspatil@google.com
+hridya@google.com
diff --git a/libion/ion.c b/libion/ion.c
index b8de5a4..1ecfc78 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -152,6 +152,8 @@
ion_user_handle_t handle;
int ret;
+ if (!handle_fd) return -EINVAL;
+
if (!ion_is_legacy(fd)) {
struct ion_new_allocation_data data = {
.len = len,
@@ -201,6 +203,7 @@
int ret;
struct ion_heap_query query;
+ if (!cnt) return -EINVAL;
memset(&query, 0, sizeof(query));
ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
index 6ae79d4..614510c 100644
--- a/libion/ion_4.12.h
+++ b/libion/ion_4.12.h
@@ -1,125 +1,50 @@
-/*
- * Adapted from drivers/staging/android/uapi/ion.h
- *
- * Copyright (C) 2011 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
#ifndef _UAPI_LINUX_ION_NEW_H
#define _UAPI_LINUX_ION_NEW_H
-
#include <linux/ioctl.h>
#include <linux/types.h>
-
#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
-
-/**
- * DOC: Ion Userspace API
- *
- * create a client by opening /dev/ion
- * most operations handled via following ioctls
- *
- */
-
-/**
- * struct ion_new_allocation_data - metadata passed from userspace for allocations
- * @len: size of the allocation
- * @heap_id_mask: mask of heap ids to allocate from
- * @flags: flags passed to heap
- * @handle: pointer that will be populated with a cookie to use to
- * refer to this allocation
- *
- * Provided by userspace as an argument to the ioctl - added _new to denote
- * this belongs to the new ION interface.
- */
struct ion_new_allocation_data {
- __u64 len;
- __u32 heap_id_mask;
- __u32 flags;
- __u32 fd;
- __u32 unused;
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
};
-
#define MAX_HEAP_NAME 32
-
-/**
- * struct ion_heap_data - data about a heap
- * @name - first 32 characters of the heap name
- * @type - heap type
- * @heap_id - heap id for the heap
- */
struct ion_heap_data {
- char name[MAX_HEAP_NAME];
- __u32 type;
- __u32 heap_id;
- __u32 reserved0;
- __u32 reserved1;
- __u32 reserved2;
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
-/**
- * struct ion_heap_query - collection of data about all heaps
- * @cnt - total number of heaps to be copied
- * @heaps - buffer to copy heap data
- */
struct ion_heap_query {
- __u32 cnt; /* Total number of heaps to be copied */
- __u32 reserved0; /* align to 64bits */
- __u64 heaps; /* buffer to be populated */
- __u32 reserved1;
- __u32 reserved2;
+ __u32 cnt;
+ __u32 reserved0;
+ __u64 heaps;
+ __u32 reserved1;
+ __u32 reserved2;
};
-
#define ION_IOC_MAGIC 'I'
-
-/**
- * DOC: ION_IOC_NEW_ALLOC - allocate memory
- *
- * Takes an ion_allocation_data struct and returns it with the handle field
- * populated with the opaque handle for the allocation.
- * TODO: This IOCTL will clash by design; however, only one of
- * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
- * so this should not conflict.
- */
#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-
-/**
- * DOC: ION_IOC_FREE - free memory
- *
- * Takes an ion_handle_data struct and frees the handle.
- *
- * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
- *
- * Takes an ion_fd_data struct with the handle field populated with a valid
- * opaque handle. Returns the struct with the fd field set to a file
- * descriptor open in the current address space. This file descriptor
- * can then be passed to another process. The corresponding opaque handle can
- * be retrieved via ION_IOC_IMPORT.
- *
- * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
- * This will come from the older kernels, so don't redefine here
- */
-
-/**
- * DOC: ION_IOC_HEAP_QUERY - information about available heaps
- *
- * Takes an ion_heap_query structure and populates information about
- * available Ion heaps.
- */
#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-
-#endif /* _UAPI_LINUX_ION_NEW_H */
+#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/original-kernel-headers/linux/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+ __u32 cnt; /* Total number of heaps to be copied */
+ __u32 reserved0; /* align to 64bits */
+ __u64 heaps; /* buffer to be populated */
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ * so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index b3fcb3b..d3b4688 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -18,18 +18,15 @@
name: "ion-unit-tests",
cflags: [
"-g",
- "-Wall",
- "-Werror",
"-Wno-missing-field-initializers",
],
shared_libs: ["libion"],
srcs: [
- "ion_test_fixture.cpp",
"allocate_test.cpp",
- "formerly_valid_handle_test.cpp",
- "invalid_values_test.cpp",
- "map_test.cpp",
- "device_test.cpp",
"exit_test.cpp",
+ "heap_query.cpp",
+ "invalid_values_test.cpp",
+ "ion_test_fixture.cpp",
+ "map_test.cpp",
],
}
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index 3c4524e..5ed01bb 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,95 +14,106 @@
* limitations under the License.
*/
-#include <memory>
#include <sys/mman.h>
+#include <memory>
#include <gtest/gtest.h>
#include <ion/ion.h>
#include "ion_test_fixture.h"
-class Allocate : public IonAllHeapsTest {
-};
+class Allocate : public IonTest {};
-TEST_F(Allocate, Allocate)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, Allocate) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- ion_user_handle_t handle = 0;
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
- ASSERT_TRUE(handle != 0);
- ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ int fd;
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+ ASSERT_TRUE(fd != 0);
+ ASSERT_EQ(close(fd), 0); // free the buffer
}
}
}
-TEST_F(Allocate, AllocateCached)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCached) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- ion_user_handle_t handle = 0;
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
- ASSERT_TRUE(handle != 0);
- ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ int fd;
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), ION_FLAG_CACHED, &fd));
+ ASSERT_TRUE(fd != 0);
+ ASSERT_EQ(close(fd), 0); // free the buffer
}
}
}
-TEST_F(Allocate, AllocateCachedNeedsSync)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCachedNeedsSync) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- ion_user_handle_t handle = 0;
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
- ASSERT_TRUE(handle != 0);
- ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ int fd;
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+ ION_FLAG_CACHED_NEEDS_SYNC, &fd));
+ ASSERT_TRUE(fd != 0);
+ ASSERT_EQ(close(fd), 0); // free the buffer
}
}
}
-TEST_F(Allocate, RepeatedAllocate)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, RepeatedAllocate) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- ion_user_handle_t handle = 0;
+ int fd;
for (unsigned int i = 0; i < 1024; i++) {
SCOPED_TRACE(::testing::Message() << "iteration " << i);
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
- ASSERT_TRUE(handle != 0);
- ASSERT_EQ(0, ion_free(m_ionFd, handle));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+ ASSERT_TRUE(fd != 0);
+ ASSERT_EQ(close(fd), 0); // free the buffer
}
}
}
}
-TEST_F(Allocate, Zeroed)
-{
+TEST_F(Allocate, Large) {
+ for (const auto& heap : ion_heaps) {
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+ int fd;
+ ASSERT_EQ(-ENOMEM,
+ ion_alloc_fd(ionfd, 3UL * 1024 * 1024 * 1024, 0, (1 << heap.heap_id), 0, &fd));
+ }
+}
+
+// Make sure all heaps always return zeroed pages
+TEST_F(Allocate, Zeroed) {
auto zeroes_ptr = std::make_unique<char[]>(4096);
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ for (const auto& heap : ion_heaps) {
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
int fds[16];
for (unsigned int i = 0; i < 16; i++) {
int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
ASSERT_GE(map_fd, 0);
- void *ptr = NULL;
+ void* ptr = NULL;
ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
@@ -116,13 +127,13 @@
ASSERT_EQ(0, close(fds[i]));
}
- int newIonFd = ion_open();
+ int new_ionfd = ion_open();
int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(new_ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
ASSERT_GE(map_fd, 0);
- void *ptr = NULL;
+ void* ptr = NULL;
ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
@@ -130,14 +141,6 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Allocate, Large)
-{
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- ion_user_handle_t handle = 0;
- ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+ ASSERT_EQ(0, ion_close(new_ionfd));
}
}
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
deleted file mode 100644
index eb3f7b6..0000000
--- a/libion/tests/device_test.cpp
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <memory>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <linux/ion_test.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
-
-class Device : public IonAllHeapsTest {
- public:
- virtual void SetUp();
- virtual void TearDown();
- int m_deviceFd;
- void readDMA(int fd, void *buf, size_t size);
- void writeDMA(int fd, void *buf, size_t size);
- void readKernel(int fd, void *buf, size_t size);
- void writeKernel(int fd, void *buf, size_t size);
- void blowCache();
- void dirtyCache(void *ptr, size_t size);
-};
-
-void Device::SetUp()
-{
- IonAllHeapsTest::SetUp();
- m_deviceFd = open("/dev/ion-test", O_RDONLY);
- ASSERT_GE(m_deviceFd, 0);
-}
-
-void Device::TearDown()
-{
- ASSERT_EQ(0, close(m_deviceFd));
- IonAllHeapsTest::TearDown();
-}
-
-void Device::readDMA(int fd, void *buf, size_t size)
-{
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
- struct ion_test_rw_data ion_test_rw_data = {
- .ptr = (uint64_t)buf,
- .offset = 0,
- .size = size,
- .write = 0,
- };
-
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeDMA(int fd, void *buf, size_t size)
-{
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
- struct ion_test_rw_data ion_test_rw_data = {
- .ptr = (uint64_t)buf,
- .offset = 0,
- .size = size,
- .write = 1,
- };
-
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::readKernel(int fd, void *buf, size_t size)
-{
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
- struct ion_test_rw_data ion_test_rw_data = {
- .ptr = (uint64_t)buf,
- .offset = 0,
- .size = size,
- .write = 0,
- };
-
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeKernel(int fd, void *buf, size_t size)
-{
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
- struct ion_test_rw_data ion_test_rw_data = {
- .ptr = (uint64_t)buf,
- .offset = 0,
- .size = size,
- .write = 1,
- };
-
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
- ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::blowCache()
-{
- const size_t bigger_than_cache = 8*1024*1024;
- void *buf1 = malloc(bigger_than_cache);
- void *buf2 = malloc(bigger_than_cache);
- memset(buf1, 0xaa, bigger_than_cache);
- memcpy(buf2, buf1, bigger_than_cache);
- free(buf1);
- free(buf2);
-}
-
-void Device::dirtyCache(void *ptr, size_t size)
-{
- /* try to dirty cache lines */
- for (size_t i = size-1; i > 0; i--) {
- ((volatile char *)ptr)[i];
- ((char *)ptr)[i] = i;
- }
-}
-
-TEST_F(Device, KernelReadCached)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- ((char*)buf)[4096] = 0x12;
- readKernel(map_fd, buf, 4096);
- ASSERT_EQ(((char*)buf)[4096], 0x12);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, KernelWriteCached)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeKernel(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMAReadCached)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- readDMA(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMAWriteCached)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeDMA(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, KernelReadCachedNeedsSync)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- ((char*)buf)[4096] = 0x12;
- readKernel(map_fd, buf, 4096);
- ASSERT_EQ(((char*)buf)[4096], 0x12);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, KernelWriteCachedNeedsSync)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeKernel(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMAReadCachedNeedsSync)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- ion_sync_fd(m_ionFd, map_fd);
-
- readDMA(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMAWriteCachedNeedsSync)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeDMA(map_fd, buf, 4096);
-
- ion_sync_fd(m_ionFd, map_fd);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-TEST_F(Device, KernelRead)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = 0;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- ((char*)buf)[4096] = 0x12;
- readKernel(map_fd, buf, 4096);
- ASSERT_EQ(((char*)buf)[4096], 0x12);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, KernelWrite)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = 0;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeKernel(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMARead)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = 0;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- for (int i = 0; i < 4096; i++)
- ((char *)ptr)[i] = i;
-
- readDMA(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)buf)[i]);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, DMAWrite)
-{
- auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
- for (int i = 0; i < 4096; i++)
- ((char *)buf)[i] = i;
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = 0;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- writeDMA(map_fd, buf, 4096);
-
- for (int i = 0; i < 4096; i++)
- ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
-
-TEST_F(Device, IsCached)
-{
- auto buf_ptr = std::make_unique<char[]>(4096);
- void *buf = buf_ptr.get();
-
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- int map_fd = -1;
- unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- dirtyCache(ptr, 4096);
-
- readDMA(map_fd, buf, 4096);
-
- bool same = true;
- for (int i = 4096-16; i >= 0; i -= 16)
- if (((char *)buf)[i] != i)
- same = false;
- ASSERT_FALSE(same);
-
- ASSERT_EQ(0, munmap(ptr, 4096));
- ASSERT_EQ(0, close(map_fd));
- }
-}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
index cdd3e27..f312389 100644
--- a/libion/tests/exit_test.cpp
+++ b/libion/tests/exit_test.cpp
@@ -22,206 +22,206 @@
#include "ion_test_fixture.h"
-class Exit : public IonAllHeapsTest {
-};
+class Exit : public IonTest {};
-TEST_F(Exit, WithAlloc)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithAllocFd) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- ion_user_handle_t handle = 0;
+ EXPECT_EXIT(
+ {
+ int handle_fd = -1;
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
- ASSERT_TRUE(handle != 0);
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
+ ASSERT_EQ(0,
+ ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &handle_fd));
+ ASSERT_NE(-1, handle_fd);
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
}
}
}
-TEST_F(Exit, WithAllocFd)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
- for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int handle_fd = -1;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
- ASSERT_NE(-1, handle_fd);
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
- }
- }
-}
-
-TEST_F(Exit, WithRepeatedAllocFd)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithRepeatedAllocFd) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
for (unsigned int i = 0; i < 1024; i++) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- ASSERT_EXIT({
- int handle_fd = -1;
+ ASSERT_EXIT(
+ {
+ int handle_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
- ASSERT_NE(-1, handle_fd);
- exit(0);
- }, ::testing::ExitedWithCode(0), "")
- << "failed on heap " << heapMask
- << " and size " << size
- << " on iteration " << i;
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0,
+ &handle_fd));
+ ASSERT_NE(-1, handle_fd);
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "")
+ << "failed on heap " << heap.name << ":" << heap.type << ":" << heap.heap_id
+ << " and size " << size << " on iteration " << i;
}
}
}
}
-
-TEST_F(Exit, WithMapping)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMapping) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
- ASSERT_GE(map_fd, 0);
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
- }
- }
-
-}
-
-TEST_F(Exit, WithPartialMapping)
-{
- static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
- for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- ASSERT_EQ(0, munmap(ptr, size / 2));
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
}
}
}
-TEST_F(Exit, WithMappingCached)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMapping) {
+ static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
- ASSERT_GE(map_fd, 0);
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+ ASSERT_GE(map_fd, 0);
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
- }
- }
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
-}
-
-TEST_F(Exit, WithPartialMappingCached)
-{
- static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
- for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
-
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
- ASSERT_GE(map_fd, 0);
-
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
-
- ASSERT_EQ(0, munmap(ptr, size / 2));
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
}
}
}
-TEST_F(Exit, WithMappingNeedsSync)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMappingCached) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
- ASSERT_GE(map_fd, 0);
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+ ION_FLAG_CACHED, &map_fd));
+ ASSERT_GE(map_fd, 0);
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
}
}
-
}
-TEST_F(Exit, WithPartialMappingNeedsSync)
-{
- static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMappingCached) {
+ static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
- EXPECT_EXIT({
- int map_fd = -1;
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
- ASSERT_GE(map_fd, 0);
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+ ION_FLAG_CACHED, &map_fd));
+ ASSERT_GE(map_fd, 0);
- void *ptr;
- ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
- ASSERT_TRUE(ptr != NULL);
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
- ASSERT_EQ(0, munmap(ptr, size / 2));
- exit(0);
- }, ::testing::ExitedWithCode(0), "");
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithMappingNeedsSync) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+ ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+ &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
+ }
+ }
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync) {
+ static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
+ for (size_t size : allocationSizes) {
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+ SCOPED_TRACE(::testing::Message() << "size " << size);
+ EXPECT_EXIT(
+ {
+ int map_fd = -1;
+
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+ ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+ &map_fd));
+ ASSERT_GE(map_fd, 0);
+
+ void* ptr;
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ ASSERT_TRUE(ptr != NULL);
+
+ ASSERT_EQ(0, munmap(ptr, size / 2));
+ exit(0);
+ },
+ ::testing::ExitedWithCode(0), "");
}
}
}
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
deleted file mode 100644
index 01ab8f3..0000000
--- a/libion/tests/formerly_valid_handle_test.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/mman.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-class FormerlyValidHandle : public IonTest {
- public:
- virtual void SetUp();
- virtual void TearDown();
- ion_user_handle_t m_handle;
-};
-
-void FormerlyValidHandle::SetUp()
-{
- IonTest::SetUp();
- ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
- ASSERT_TRUE(m_handle != 0);
- ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
-}
-
-void FormerlyValidHandle::TearDown()
-{
- m_handle = 0;
-}
-
-TEST_F(FormerlyValidHandle, free)
-{
- ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
-}
-
-TEST_F(FormerlyValidHandle, map)
-{
- int map_fd;
- unsigned char *ptr;
-
- ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-}
-
-TEST_F(FormerlyValidHandle, share)
-{
- int share_fd;
-
- ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
-}
diff --git a/libutils/tests/Mutex_test.cpp b/libion/tests/heap_query.cpp
similarity index 66%
copy from libutils/tests/Mutex_test.cpp
copy to libion/tests/heap_query.cpp
index 8a1805f..bad3bbf 100644
--- a/libutils/tests/Mutex_test.cpp
+++ b/libion/tests/heap_query.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,14 @@
* limitations under the License.
*/
-#include <utils/Mutex.h>
-
#include <gtest/gtest.h>
+#include "ion_test_fixture.h"
-static android::Mutex mLock;
-static int i GUARDED_BY(mLock);
+class HeapQuery : public IonTest {};
-void modifyLockedVariable() REQUIRES(mLock) {
- i = 1;
+TEST_F(HeapQuery, AtleastOneHeap) {
+ ASSERT_GT(ion_heaps.size(), 0);
}
-TEST(Mutex, compile) {
- android::Mutex::Autolock _l(mLock);
- i = 0;
- modifyLockedVariable();
-}
\ No newline at end of file
+// TODO: Check if we expect some of the default
+// heap types to be present on all devices.
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
index 77fea17..48fcd72 100644
--- a/libion/tests/invalid_values_test.cpp
+++ b/libion/tests/invalid_values_test.cpp
@@ -16,171 +16,71 @@
#include <sys/mman.h>
+#include <memory>
+#include <vector>
+
#include <gtest/gtest.h>
#include <ion/ion.h>
-
#include "ion_test_fixture.h"
-class InvalidValues : public IonAllHeapsTest {
- public:
- virtual void SetUp();
- virtual void TearDown();
- ion_user_handle_t m_validHandle;
- int m_validShareFd;
- ion_user_handle_t const m_badHandle = -1;
-};
+class InvalidValues : public IonTest {};
-void InvalidValues::SetUp()
-{
- IonAllHeapsTest::SetUp();
- ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
- << m_ionFd << " " << m_firstHeap;
- ASSERT_TRUE(m_validHandle != 0);
- ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
-}
-
-void InvalidValues::TearDown()
-{
- ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
- ASSERT_EQ(0, close(m_validShareFd));
- m_validHandle = 0;
- IonAllHeapsTest::TearDown();
-}
-
-TEST_F(InvalidValues, ion_close)
-{
+TEST_F(InvalidValues, ion_close) {
EXPECT_EQ(-EBADF, ion_close(-1));
}
-TEST_F(InvalidValues, ion_alloc)
-{
- ion_user_handle_t handle;
- /* invalid ion_fd */
- int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion_fd */
- EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
- /* no heaps */
- EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- /* zero size */
- EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
- /* too large size */
- EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
- /* bad alignment */
- EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
- /* NULL handle */
- EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
- }
-}
-
-TEST_F(InvalidValues, ion_alloc_fd)
-{
+TEST_F(InvalidValues, ion_alloc_fd) {
int fd;
- /* invalid ion_fd */
- int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion_fd */
- EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
- /* no heaps */
- EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- /* zero size */
- EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
- /* too large size */
- EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
- /* bad alignment */
- EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
- /* NULL handle */
- EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+ // no heaps
+ EXPECT_EQ(-ENODEV, ion_alloc_fd(ionfd, 4096, 0, 0, 0, &fd));
+ for (const auto& heap : ion_heaps) {
+ // invalid ion_fd
+ int ret = ion_alloc_fd(0, 4096, 0, (1 << heap.heap_id), 0, &fd);
+ EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+ // invalid ion_fd
+ EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, (1 << heap.heap_id), 0, &fd));
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+ // zero size
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 0, 0, (1 << heap.heap_id), 0, &fd));
+ // too large size
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, -1, 0, (1 << heap.heap_id), 0, &fd));
+ // bad alignment
+ // TODO: Current userspace and kernel code completely ignores alignment. So this
+ // test is going to fail. We need to completely remove alignment from the API.
+ // All memory by default is always page aligned. OR actually pass the alignment
+ // down into the kernel and make kernel respect the alignment.
+ // EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, -1, (1 << heap.heap_id), 0, &fd));
+
+ // NULL fd
+ EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, nullptr));
}
}
-TEST_F(InvalidValues, ion_free)
-{
- /* invalid ion fd */
- int ret = ion_free(0, m_validHandle);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion fd */
- EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
- /* zero handle */
- EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
- /* bad handle */
- EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+TEST_F(InvalidValues, ion_query_heap_cnt) {
+ // NULL count
+ EXPECT_EQ(-EINVAL, ion_query_heap_cnt(ionfd, nullptr));
+
+ int heap_count;
+ // bad fd
+ EXPECT_EQ(-EBADF, ion_query_heap_cnt(-1, &heap_count));
}
-TEST_F(InvalidValues, ion_map)
-{
- int map_fd;
- unsigned char *ptr;
+TEST_F(InvalidValues, ion_query_get_heaps) {
+ int heap_count;
+ ASSERT_EQ(0, ion_query_heap_cnt(ionfd, &heap_count));
+ ASSERT_GT(heap_count, 0);
- /* invalid ion fd */
- int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion fd */
- EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
- /* zero handle */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
- /* bad handle */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
- /* zero length */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
- /* bad prot */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
- /* bad offset */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
- /* NULL ptr */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
- /* NULL map_fd */
- EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
-}
+ // nullptr buffers, still returns success but without
+ // the ion_heap_data.
+ EXPECT_EQ(0, ion_query_get_heaps(ionfd, heap_count, nullptr));
-TEST_F(InvalidValues, ion_share)
-{
- int share_fd;
+ std::unique_ptr<struct ion_heap_data[]> heaps =
+ std::make_unique<struct ion_heap_data[]>(heap_count);
+ // bad fd
+ EXPECT_EQ(-EBADF, ion_query_get_heaps(-1, heap_count, heaps.get()));
- /* invalid ion fd */
- int ret = ion_share(0, m_validHandle, &share_fd);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion fd */
- EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
- /* zero handle */
- EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
- /* bad handle */
- EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
- /* NULL share_fd */
- EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
-}
-
-TEST_F(InvalidValues, ion_import)
-{
- ion_user_handle_t handle;
-
- /* invalid ion fd */
- int ret = ion_import(0, m_validShareFd, &handle);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion fd */
- EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
- /* bad share_fd */
- EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
- /* invalid share_fd */
- EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
- /* NULL handle */
- EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
-}
-
-TEST_F(InvalidValues, ion_sync_fd)
-{
- /* invalid ion fd */
- int ret = ion_sync_fd(0, m_validShareFd);
- EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
- /* invalid ion fd */
- EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
- /* bad share_fd */
- EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
- /* invalid share_fd */
- EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+ // invalid heap data pointer
+ EXPECT_EQ(-EFAULT, ion_query_get_heaps(ionfd, heap_count, reinterpret_cast<void*>(0xdeadf00d)));
}
diff --git a/libion/tests/ion_4.12.h b/libion/tests/ion_4.12.h
new file mode 100644
index 0000000..614510c
--- /dev/null
+++ b/libion/tests/ion_4.12.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+struct ion_new_allocation_data {
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
+};
+#define MAX_HEAP_NAME 32
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+struct ion_heap_query {
+ __u32 cnt;
+ __u32 reserved0;
+ __u64 heaps;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+#define ION_IOC_MAGIC 'I'
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+#endif
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
index e20c730..935fe5c 100644
--- a/libion/tests/ion_test_fixture.cpp
+++ b/libion/tests/ion_test_fixture.cpp
@@ -15,59 +15,26 @@
*/
#include <gtest/gtest.h>
-
#include <ion/ion.h>
#include "ion_test_fixture.h"
-IonTest::IonTest() : m_ionFd(-1)
-{
-}
+IonTest::IonTest() : ionfd(-1), ion_heaps() {}
void IonTest::SetUp() {
- m_ionFd = ion_open();
- ASSERT_GE(m_ionFd, 0);
+ ionfd = ion_open();
+ ASSERT_GE(ionfd, 0);
+
+ int heap_count;
+ int ret = ion_query_heap_cnt(ionfd, &heap_count);
+ ASSERT_EQ(ret, 0);
+ ASSERT_GT(heap_count, 0);
+
+ ion_heaps.resize(heap_count, {});
+ ret = ion_query_get_heaps(ionfd, heap_count, ion_heaps.data());
+ ASSERT_EQ(ret, 0);
}
void IonTest::TearDown() {
- ion_close(m_ionFd);
-}
-
-IonAllHeapsTest::IonAllHeapsTest() :
- m_firstHeap(0),
- m_lastHeap(0),
- m_allHeaps()
-{
-}
-
-void IonAllHeapsTest::SetUp() {
- int fd = ion_open();
- ASSERT_GE(fd, 0);
-
- for (int i = 1; i != 0; i <<= 1) {
- ion_user_handle_t handle = 0;
- int ret;
- ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
- if (ret == 0 && handle != 0) {
- ion_free(fd, handle);
- if (!m_firstHeap) {
- m_firstHeap = i;
- }
- m_lastHeap = i;
- m_allHeaps.push_back(i);
- } else {
- ASSERT_EQ(-ENODEV, ret);
- }
- }
- ion_close(fd);
-
- EXPECT_NE(0U, m_firstHeap);
- EXPECT_NE(0U, m_lastHeap);
-
- RecordProperty("Heaps", m_allHeaps.size());
- IonTest::SetUp();
-}
-
-void IonAllHeapsTest::TearDown() {
- IonTest::TearDown();
+ ion_close(ionfd);
}
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index 4098214..4f254b8 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,29 +18,19 @@
#define ION_TEST_FIXTURE_H_
#include <gtest/gtest.h>
+#include <vector>
+#include "ion_4.12.h"
using ::testing::Test;
class IonTest : public virtual Test {
- public:
+ public:
IonTest();
- virtual ~IonTest() {};
- virtual void SetUp();
- virtual void TearDown();
- int m_ionFd;
-};
-
-class IonAllHeapsTest : public IonTest {
- public:
- IonAllHeapsTest();
- virtual ~IonAllHeapsTest() {};
+ virtual ~IonTest(){};
virtual void SetUp();
virtual void TearDown();
-
- unsigned int m_firstHeap;
- unsigned int m_lastHeap;
-
- std::vector<unsigned int> m_allHeaps;
+ int ionfd;
+ std::vector<struct ion_heap_data> ion_heaps;
};
#endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
index c006dc8..f1b47b7 100644
--- a/libion/tests/map_test.cpp
+++ b/libion/tests/map_test.cpp
@@ -15,61 +15,30 @@
*/
#include <sys/mman.h>
+#include <unistd.h>
#include <gtest/gtest.h>
#include <ion/ion.h>
-
#include "ion_test_fixture.h"
-class Map : public IonAllHeapsTest {
-};
+class Map : public IonTest {};
-TEST_F(Map, MapHandle)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapFd) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
- SCOPED_TRACE(::testing::Message() << "size " << size);
- ion_user_handle_t handle = 0;
-
- ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
- ASSERT_TRUE(handle != 0);
-
- int map_fd = -1;
- unsigned char *ptr = NULL;
- ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
- ASSERT_TRUE(ptr != NULL);
- ASSERT_GE(map_fd, 0);
-
- ASSERT_EQ(0, close(map_fd));
-
- ASSERT_EQ(0, ion_free(m_ionFd, handle));
-
- memset(ptr, 0xaa, size);
-
- ASSERT_EQ(0, munmap(ptr, size));
- }
- }
-}
-
-TEST_F(Map, MapFd)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
- for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
ASSERT_GE(map_fd, 0);
- void *ptr;
+ void* ptr;
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
-
ASSERT_EQ(0, close(map_fd));
memset(ptr, 0xaa, size);
@@ -79,53 +48,51 @@
}
}
-TEST_F(Map, MapOffset)
-{
- for (unsigned int heapMask : m_allHeaps) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+TEST_F(Map, MapOffset) {
+ for (const auto& heap : ion_heaps) {
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
int map_fd = -1;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize() * 2, 0, (1 << heap.heap_id), 0, &map_fd));
ASSERT_GE(map_fd, 0);
- unsigned char *ptr;
- ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+ unsigned char* ptr;
+ ptr = (unsigned char*)mmap(NULL, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
+ map_fd, 0);
ASSERT_TRUE(ptr != NULL);
- memset(ptr, 0, PAGE_SIZE);
- memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+ memset(ptr, 0, getpagesize());
+ memset(ptr + getpagesize(), 0xaa, getpagesize());
- ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+ ASSERT_EQ(0, munmap(ptr, getpagesize() * 2));
- ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+ ptr = (unsigned char*)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, map_fd,
+ getpagesize());
ASSERT_TRUE(ptr != NULL);
-
ASSERT_EQ(ptr[0], 0xaa);
- ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
-
- ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
-
+ ASSERT_EQ(ptr[getpagesize() - 1], 0xaa);
+ ASSERT_EQ(0, munmap(ptr, getpagesize()));
ASSERT_EQ(0, close(map_fd));
}
}
-TEST_F(Map, MapCached)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCached) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
int map_fd = -1;
unsigned int flags = ION_FLAG_CACHED;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
ASSERT_GE(map_fd, 0);
- void *ptr;
+ void* ptr;
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
-
ASSERT_EQ(0, close(map_fd));
memset(ptr, 0xaa, size);
@@ -135,23 +102,22 @@
}
}
-TEST_F(Map, MapCachedNeedsSync)
-{
- static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
- for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCachedNeedsSync) {
+ static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+ for (const auto& heap : ion_heaps) {
for (size_t size : allocationSizes) {
- SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+ SCOPED_TRACE(::testing::Message()
+ << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
SCOPED_TRACE(::testing::Message() << "size " << size);
int map_fd = -1;
unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
- ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+ ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
ASSERT_GE(map_fd, 0);
- void *ptr;
+ void* ptr;
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
-
ASSERT_EQ(0, close(map_fd));
memset(ptr, 0xaa, size);
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index b388e95..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -16,3 +16,17 @@
srcs: ["keyutils_test.cpp"],
test_suites: ["device-tests"],
}
+
+cc_binary {
+ name: "mini-keyctl",
+ srcs: [
+ "mini_keyctl.cpp",
+ "mini_keyctl_utils.cpp"
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ "liblog",
+ ],
+ cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
+}
diff --git a/libkeyutils/include/keyutils.h b/libkeyutils/include/keyutils.h
index 585767d..c508f27 100644
--- a/libkeyutils/include/keyutils.h
+++ b/libkeyutils/include/keyutils.h
@@ -51,6 +51,10 @@
long keyctl_unlink(key_serial_t key, key_serial_t keyring);
+long keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction);
+
+long keyctl_get_security(key_serial_t key, char* buffer, size_t buflen);
+
__END_DECLS
#endif
diff --git a/libkeyutils/keyutils.cpp b/libkeyutils/keyutils.cpp
index 58a2a17..8f63f70 100644
--- a/libkeyutils/keyutils.cpp
+++ b/libkeyutils/keyutils.cpp
@@ -69,3 +69,11 @@
long keyctl_unlink(key_serial_t key, key_serial_t keyring) {
return keyctl(KEYCTL_UNLINK, key, keyring);
}
+
+long keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction) {
+ return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
+}
+
+long keyctl_get_security(key_serial_t id, char* buffer, size_t buflen) {
+ return keyctl(KEYCTL_GET_SECURITY, id, buffer, buflen);
+}
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
new file mode 100644
index 0000000..fe89e62
--- /dev/null
+++ b/libkeyutils/mini_keyctl.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * A tool loads keys to keyring.
+ */
+
+#include "mini_keyctl_utils.h"
+
+#include <error.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <android-base/parseint.h>
+
+static void Usage(int exit_code) {
+ fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+ fprintf(stderr, " mini-keyctl add <type> <desc> <data> <keyring>\n");
+ fprintf(stderr, " mini-keyctl padd <type> <desc> <keyring>\n");
+ fprintf(stderr, " mini-keyctl unlink <key> <keyring>\n");
+ fprintf(stderr, " mini-keyctl restrict_keyring <keyring>\n");
+ fprintf(stderr, " mini-keyctl security <key>\n");
+ _exit(exit_code);
+}
+
+static key_serial_t parseKeyOrDie(const char* str) {
+ key_serial_t key;
+ if (!android::base::ParseInt(str, &key)) {
+ error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+ }
+ return key;
+}
+
+int main(int argc, const char** argv) {
+ if (argc < 2) Usage(1);
+ const std::string action = argv[1];
+
+ if (action == "add") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string data = argv[4];
+ std::string keyring = argv[5];
+ return Add(type, desc, data, keyring);
+ } else if (action == "padd") {
+ if (argc != 5) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string keyring = argv[4];
+ return Padd(type, desc, keyring);
+ } else if (action == "restrict_keyring") {
+ if (argc != 3) Usage(1);
+ std::string keyring = argv[2];
+ return RestrictKeyring(keyring);
+ } else if (action == "unlink") {
+ if (argc != 4) Usage(1);
+ key_serial_t key = parseKeyOrDie(argv[2]);
+ const std::string keyring = argv[3];
+ return Unlink(key, keyring);
+ } else if (action == "security") {
+ if (argc != 3) Usage(1);
+ const char* key_str = argv[2];
+ key_serial_t key = parseKeyOrDie(key_str);
+ std::string context = RetrieveSecurityContext(key);
+ if (context.empty()) {
+ perror(key_str);
+ return 1;
+ }
+ fprintf(stderr, "%s\n", context.c_str());
+ return 0;
+ } else {
+ fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
+ Usage(1);
+ }
+
+ return 0;
+}
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..79b4680
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mini_keyctl_utils.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <keyutils.h>
+
+static constexpr int kMaxCertSize = 4096;
+
+static std::vector<std::string> SplitBySpace(const std::string& s) {
+ std::istringstream iss(s);
+ return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+ std::istream_iterator<std::string>{}};
+}
+
+// Find the keyring id. Because request_key(2) syscall is not available or the key is
+// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
+// information in the descritption section depending on the key type, only the first word in the
+// keyring description is used for searching.
+static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
+ // If the keyring id is already a hex number, directly convert it to keyring id
+ key_serial_t keyring_id;
+ if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
+ return keyring_id;
+ }
+
+ // Only keys allowed by SELinux rules will be shown here.
+ std::ifstream proc_keys_file("/proc/keys");
+ if (!proc_keys_file.is_open()) {
+ error(1, errno, "Failed to open /proc/keys");
+ return -1;
+ }
+
+ std::string line;
+ while (getline(proc_keys_file, line)) {
+ std::vector<std::string> tokens = SplitBySpace(line);
+ if (tokens.size() < 9) {
+ continue;
+ }
+ std::string key_id = "0x" + tokens[0];
+ std::string key_type = tokens[7];
+ // The key description may contain space.
+ std::string key_desc_prefix = tokens[8];
+ // The prefix has a ":" at the end
+ std::string key_desc_pattern = keyring_desc + ":";
+ if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+ continue;
+ }
+ if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
+ error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
+ return -1;
+ }
+ return keyring_id;
+ }
+ return -1;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ if (keyctl_unlink(key, keyring_id) < 0) {
+ error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
+ return 1;
+ }
+ return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring) {
+ if (data.size() > kMaxCertSize) {
+ error(1, 0, "Certificate too large");
+ return 1;
+ }
+
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+
+ // read from stdin to get the certificates
+ std::istreambuf_iterator<char> begin(std::cin), end;
+ std::string data(begin, end);
+
+ if (data.size() > kMaxCertSize) {
+ error(1, 0, "Certificate too large");
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+ key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
+ return 1;
+ }
+ return 0;
+}
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+ // Simply assume this size is enough in practice.
+ const int kMaxSupportedSize = 256;
+ std::string context;
+ context.resize(kMaxSupportedSize);
+ long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+ if (retval < 0) {
+ error(1, errno, "Cannot get security context of key %x", key);
+ return std::string();
+ }
+ if (retval > kMaxSupportedSize) {
+ error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
+ return std::string();
+ }
+ context.resize(retval);
+ return context;
+}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
new file mode 100644
index 0000000..3616831
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/keyutils.h"
+
+#include <string>
+
+// Add key to a keyring. Returns non-zero if error happens.
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring);
+
+// Add key from stdin to a keyring. Returns non-zero if error happens.
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
+
+// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
+int Unlink(key_serial_t key, const std::string& keyring);
+
+// Apply key-linking to a keyring. Return non-zero if error happens.
+int RestrictKeyring(const std::string& keyring);
+
+// Retrieves a key's security context. Return the context string, or empty string on error.
+std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/.clang-format b/liblog/.clang-format
deleted file mode 100644
index 9db87a8..0000000
--- a/liblog/.clang-format
+++ /dev/null
@@ -1,9 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-PointerAlignment: Left
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/demangle/.clang-format b/liblog/.clang-format
similarity index 100%
rename from demangle/.clang-format
rename to liblog/.clang-format
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 4a165a0..53d3ab3 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,31 +15,29 @@
//
liblog_sources = [
- "config_read.c",
- "config_write.c",
- "local_logger.c",
- "log_event_list.c",
- "log_event_write.c",
- "log_ratelimit.cpp",
- "logger_lock.c",
- "logger_name.c",
- "logger_read.c",
- "logger_write.c",
- "logprint.c",
- "stderr_write.c",
+ "config_read.cpp",
+ "config_write.cpp",
+ "log_event_list.cpp",
+ "log_event_write.cpp",
+ "logger_lock.cpp",
+ "logger_name.cpp",
+ "logger_read.cpp",
+ "logger_write.cpp",
+ "logprint.cpp",
+ "stderr_write.cpp",
]
liblog_host_sources = [
- "fake_log_device.c",
- "fake_writer.c",
+ "fake_log_device.cpp",
+ "fake_writer.cpp",
]
liblog_target_sources = [
"event_tag_map.cpp",
"log_time.cpp",
- "properties.c",
- "pmsg_reader.c",
- "pmsg_writer.c",
- "logd_reader.c",
- "logd_writer.c",
+ "properties.cpp",
+ "pmsg_reader.cpp",
+ "pmsg_writer.cpp",
+ "logd_reader.cpp",
+ "logd_writer.cpp",
]
cc_library_headers {
@@ -47,7 +45,10 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
+ system_shared_libs: [],
+ stl: "none",
target: {
windows: {
enabled: true,
@@ -67,6 +68,7 @@
name: "liblog",
host_supported: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: liblog_sources,
target: {
@@ -75,6 +77,7 @@
cflags: ["-DFAKE_LOG_DEVICE=1"],
},
android: {
+ version_script: "liblog.map.txt",
srcs: liblog_target_sources,
// AddressSanitizer runtime library depends on liblog.
sanitize: {
@@ -87,7 +90,6 @@
ldflags: ["-Wl,--hash-style=both"],
},
windows: {
- srcs: ["uio.c"],
enabled: true,
},
not_windows: {
@@ -101,9 +103,13 @@
header_libs: ["liblog_headers"],
export_header_lib_headers: ["liblog_headers"],
+ stubs: {
+ symbol_file: "liblog.map.txt",
+ versions: ["10000"],
+ },
+
cflags: [
"-Werror",
- "-fvisibility=hidden",
// This is what we want to do:
// liblog_cflags := $(shell \
// sed -n \
@@ -134,7 +140,7 @@
llndk_library {
name: "liblog",
+ native_bridge_supported: true,
symbol_file: "liblog.map.txt",
- unversioned: true,
export_include_dirs: ["include_vndk"],
}
diff --git a/liblog/Android.mk b/liblog/Android.mk
deleted file mode 100644
index 6c4dff5..0000000
--- a/liblog/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH := $(my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/README b/liblog/README
deleted file mode 100644
index 5a845be..0000000
--- a/liblog/README
+++ /dev/null
@@ -1,209 +0,0 @@
-LIBLOG(3) Android Internal NDK Programming Manual LIBLOG(3)
-
-
-
-NAME
- liblog - Android Internal NDK logger interfaces
-
-SYNOPSIS
- /*
- * Please limit to 24 characters for runtime is loggable,
- * 16 characters for persist is loggable, and logcat pretty
- * alignment with limit of 7 characters.
- */
- #define LOG_TAG "yourtag"
- #include <log/log.h>
-
- ALOG(android_priority, tag, format, ...)
- IF_ALOG(android_priority, tag)
- LOG_PRI(priority, tag, format, ...)
- LOG_PRI_VA(priority, tag, format, args)
- #define LOG_TAG NULL
- ALOGV(format, ...)
- SLOGV(format, ...)
- RLOGV(format, ...)
- ALOGV_IF(cond, format, ...)
- SLOGV_IF(cond, format, ...)
- RLOGV_IF(cond, format, ...)
- IF_ALOGC()
- ALOGD(format, ...)
- SLOGD(format, ...)
- RLOGD(format, ...)
- ALOGD_IF(cond, format, ...)
- SLOGD_IF(cond, format, ...)
- RLOGD_IF(cond, format, ...)
- IF_ALOGD()
- ALOGI(format, ...)
- SLOGI(format, ...)
- RLOGI(format, ...)
- ALOGI_IF(cond, format, ...)
- SLOGI_IF(cond, format, ...)
- RLOGI_IF(cond, format, ...)
- IF_ALOGI()
- ALOGW(format, ...)
- SLOGW(format, ...)
- RLOGW(format, ...)
- ALOGW_IF(cond, format, ...)
- SLOGW_IF(cond, format, ...)
- RLOGW_IF(cond, format, ...)
- IF_ALOGW()
- ALOGE(format, ...)
- SLOGE(format, ...)
- RLOGE(format, ...)
- ALOGE_IF(cond, format, ...)
- SLOGE_IF(cond, format, ...)
- RLOGE_IF(cond, format, ...)
- IF_ALOGE()
- LOG_FATAL(format, ...)
- LOG_ALWAYS_FATAL(format, ...)
- LOG_FATAL_IF(cond, format, ...)
- LOG_ALWAYS_FATAL_IF(cond, format, ...)
- ALOG_ASSERT(cond, format, ...)
- LOG_EVENT_INT(tag, value)
- LOG_EVENT_LONG(tag, value)
-
- clockid_t android_log_clockid()
-
- log_id_t android_logger_get_id(struct logger *logger)
- int android_logger_clear(struct logger *logger)
- int android_logger_get_log_size(struct logger *logger)
- int android_logger_get_log_readable_size(struct logger *logger)
- int android_logger_get_log_version(struct logger *logger)
-
- struct logger_list *android_logger_list_alloc(int mode,
- unsigned int tail,
- pid_t pid)
- struct logger *android_logger_open(struct logger_list *logger_list,
- log_id_t id)
- struct logger_list *android_logger_list_open(log_id_t id, int mode,
- unsigned int tail,
- pid_t pid)
- int android_logger_list_read(struct logger_list *logger_list,
- struct log_msg *log_msg)
- void android_logger_list_free(struct logger_list *logger_list)
-
- log_id_t android_name_to_log_id(const char *logName)
- const char *android_log_id_to_name(log_id_t log_id)
-
- android_log_context create_android_logger(uint32_t tag)
-
- int android_log_write_list_begin(android_log_context ctx)
- int android_log_write_list_end(android_log_context ctx)
-
- int android_log_write_int32(android_log_context ctx, int32_t value)
- int android_log_write_int64(android_log_context ctx, int64_t value)
- int android_log_write_string8(android_log_context ctx,
- const char *value)
- int android_log_write_string8_len(android_log_context ctx,
- const char *value, size_t maxlen)
- int android_log_write_float32(android_log_context ctx, float value)
-
- int android_log_write_list(android_log_context ctx,
- log_id_t id = LOG_ID_EVENTS)
-
- android_log_context create_android_log_parser(const char *msg,
- size_t len)
- android_log_list_element android_log_read_next(android_log_context ctx)
- android_log_list_element android_log_peek_next(android_log_context ctx)
-
- int android_log_destroy(android_log_context *ctx)
-
- #include <log/log_transport.h>
-
- int android_set_log_transport(int transport_flag)
- int android_get_log_transport()
-
- Link with -llog
-
-DESCRIPTION
- liblog represents an interface to the volatile Android Logging system
- for NDK (Native) applications and libraries. Interfaces for either
- writing or reading logs. The log buffers are divided up in Main, Sys‐
- tem, Radio and Events sub-logs.
-
- The logging interfaces are a series of macros, all of which can be
- overridden individually in order to control the verbosity of the appli‐
- cation or library. [ASR]LOG[VDIWE] calls are used to log to BAsic,
- System or Radio sub-logs in either the Verbose, Debug, Info, Warning or
- Error priorities. [ASR]LOG[VDIWE]_IF calls are used to perform thus
- based on a condition being true. IF_ALOG[VDIWE] calls are true if the
- current LOG_TAG is enabled at the specified priority. LOG_ALWAYS_FATAL
- is used to ALOG a message, then kill the process. LOG_FATAL call is a
- variant of LOG_ALWAYS_FATAL, only enabled in engineering, and not
- release builds. ALOG_ASSERT is used to ALOG a message if the condition
- is false; the condition is part of the logged message.
- LOG_EVENT_(INT|LONG) is used to drop binary content into the Events
- sub-log.
-
- The log reading interfaces permit opening the logs either singly or
- multiply, retrieving a log entry at a time in time sorted order,
- optionally limited to a specific pid and tail of the log(s) and finally
- a call closing the logs. A single log can be opened with android_log‐
- ger_list_open; or multiple logs can be opened with android_log‐
- ger_list_alloc, calling in turn the android_logger_open for each log
- id. Each entry can be retrieved with android_logger_list_read. The
- log(s) can be closed with android_logger_list_free. The logs should be
- opened with an ANDROID_LOG_RDONLY mode. ANDROID_LOG_NONBLOCK mode
- will report when the log reading is done with an EAGAIN error return
- code, otherwise the android_logger_list_read call will block for new
- entries.
-
- The ANDROID_LOG_WRAP mode flag to the android_logger_list_alloc_time
- signals logd to quiesce the reader until the buffer is about to prune
- at the start time then proceed to dumping content.
-
- The ANDROID_LOG_PSTORE mode flag to the android_logger_open is used to
- switch from the active logs to the persistent logs from before the last
- reboot.
-
- The value returned by android_logger_open can be used as a parameter to
- the android_logger_clear function to empty the sub-log. It is recom‐
- mended to only open log ANDROID_LOG_WRONLY in that case.
-
- The value returned by android_logger_open can be used as a parameter to
- the android_logger_get_log_(size|readable_size|version) to retrieve the
- sub-log maximum size, readable size and log buffer format protocol ver‐
- sion respectively. android_logger_get_id returns the id that was used
- when opening the sub-log. It is recommended to open the log
- ANDROID_LOG_RDONLY in these cases.
-
- android_set_log_transport() selects transport filters. Argument is
- either LOGGER_DEFAULT, LOGGER_LOGD, LOGGER_NULL or LOGGER_LOCAL. Log to
- logger daemon for default or logd, drop contents on floor, or log into
- local memory respectively. Both android_set_log_transport()
- and android_get_log_transport() return the current transport mask, or
- a negative errno for any problems.
-
-ERRORS
- If messages fail, a negative error code will be returned to the caller.
-
- The -ENOTCONN return code indicates that the logger daemon is stopped.
-
- The -EBADF return code indicates that the log access point can not be
- opened, or the log buffer id is out of range.
-
- For the -EAGAIN return code, this means that the logging message was
- temporarily backed-up either because of Denial Of Service (DOS) logging
- pressure from some chatty application or service in the Android system,
- or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen.
- To aid in diagnosing the occurence of this, a binary event from liblog
- will be sent to the log daemon once a new message can get through
- indicating how many messages were dropped as a result. Please take
- action to resolve the structural problems at the source.
-
- It is generally not advised for the caller to retry the -EAGAIN return
- code as this will only make the problem(s) worse and cause your
- application to temporarily drop to the logger daemon priority, BATCH
- scheduling policy and background task cgroup. If you require a group of
- messages to be passed atomically, merge them into one message with
- embedded newlines to the maximum length LOGGER_ENTRY_MAX_PAYLOAD.
-
- Other return codes from writing operation can be returned. Since the
- library retries on EINTR, -EINTR should never be returned.
-
-SEE ALSO
- syslogd(8), klogd, auditd(8)
-
-
-
- 08 Feb 2017 LIBLOG(3)
diff --git a/liblog/README.md b/liblog/README.md
new file mode 100644
index 0000000..98bee9f
--- /dev/null
+++ b/liblog/README.md
@@ -0,0 +1,176 @@
+Android liblog
+--------------
+
+Public Functions and Macros
+---------------------------
+
+ /*
+ * Please limit to 24 characters for runtime is loggable,
+ * 16 characters for persist is loggable, and logcat pretty
+ * alignment with limit of 7 characters.
+ */
+ #define LOG_TAG "yourtag"
+ #include <log/log.h>
+
+ ALOG(android_priority, tag, format, ...)
+ IF_ALOG(android_priority, tag)
+ LOG_PRI(priority, tag, format, ...)
+ LOG_PRI_VA(priority, tag, format, args)
+ #define LOG_TAG NULL
+ ALOGV(format, ...)
+ SLOGV(format, ...)
+ RLOGV(format, ...)
+ ALOGV_IF(cond, format, ...)
+ SLOGV_IF(cond, format, ...)
+ RLOGV_IF(cond, format, ...)
+ IF_ALOGC()
+ ALOGD(format, ...)
+ SLOGD(format, ...)
+ RLOGD(format, ...)
+ ALOGD_IF(cond, format, ...)
+ SLOGD_IF(cond, format, ...)
+ RLOGD_IF(cond, format, ...)
+ IF_ALOGD()
+ ALOGI(format, ...)
+ SLOGI(format, ...)
+ RLOGI(format, ...)
+ ALOGI_IF(cond, format, ...)
+ SLOGI_IF(cond, format, ...)
+ RLOGI_IF(cond, format, ...)
+ IF_ALOGI()
+ ALOGW(format, ...)
+ SLOGW(format, ...)
+ RLOGW(format, ...)
+ ALOGW_IF(cond, format, ...)
+ SLOGW_IF(cond, format, ...)
+ RLOGW_IF(cond, format, ...)
+ IF_ALOGW()
+ ALOGE(format, ...)
+ SLOGE(format, ...)
+ RLOGE(format, ...)
+ ALOGE_IF(cond, format, ...)
+ SLOGE_IF(cond, format, ...)
+ RLOGE_IF(cond, format, ...)
+ IF_ALOGE()
+ LOG_FATAL(format, ...)
+ LOG_ALWAYS_FATAL(format, ...)
+ LOG_FATAL_IF(cond, format, ...)
+ LOG_ALWAYS_FATAL_IF(cond, format, ...)
+ ALOG_ASSERT(cond, format, ...)
+ LOG_EVENT_INT(tag, value)
+ LOG_EVENT_LONG(tag, value)
+
+ clockid_t android_log_clockid()
+
+ log_id_t android_logger_get_id(struct logger *logger)
+ int android_logger_clear(struct logger *logger)
+ int android_logger_get_log_size(struct logger *logger)
+ int android_logger_get_log_readable_size(struct logger *logger)
+ int android_logger_get_log_version(struct logger *logger)
+
+ struct logger_list *android_logger_list_alloc(int mode, unsigned int tail, pid_t pid)
+ struct logger *android_logger_open(struct logger_list *logger_list, log_id_t id)
+ struct logger_list *android_logger_list_open(log_id_t id, int mode, unsigned int tail, pid_t pid)
+ int android_logger_list_read(struct logger_list *logger_list, struct log_msg *log_msg)
+ void android_logger_list_free(struct logger_list *logger_list)
+
+ log_id_t android_name_to_log_id(const char *logName)
+ const char *android_log_id_to_name(log_id_t log_id)
+
+ android_log_context create_android_logger(uint32_t tag)
+
+ int android_log_write_list_begin(android_log_context ctx)
+ int android_log_write_list_end(android_log_context ctx)
+
+ int android_log_write_int32(android_log_context ctx, int32_t value)
+ int android_log_write_int64(android_log_context ctx, int64_t value)
+ int android_log_write_string8(android_log_context ctx, const char *value)
+ int android_log_write_string8_len(android_log_context ctx, const char *value, size_t maxlen)
+ int android_log_write_float32(android_log_context ctx, float value)
+
+ int android_log_write_list(android_log_context ctx, log_id_t id = LOG_ID_EVENTS)
+
+ android_log_context create_android_log_parser(const char *msg, size_t len)
+ android_log_list_element android_log_read_next(android_log_context ctx)
+ android_log_list_element android_log_peek_next(android_log_context ctx)
+
+ int android_log_destroy(android_log_context *ctx)
+
+ #include <log/log_transport.h>
+
+ int android_set_log_transport(int transport_flag)
+ int android_get_log_transport()
+
+Description
+-----------
+
+liblog represents an interface to the volatile Android Logging system for NDK (Native) applications
+and libraries. Interfaces for either writing or reading logs. The log buffers are divided up in
+Main, System, Radio and Events sub-logs.
+
+The logging interfaces are a series of macros, all of which can be overridden individually in order
+to control the verbosity of the application or library. `[ASR]LOG[VDIWE]` calls are used to log to
+BAsic, System or Radio sub-logs in either the Verbose, Debug, Info, Warning or Error priorities.
+`[ASR]LOG[VDIWE]_IF` calls are used to perform thus based on a condition being true.
+`IF_ALOG[VDIWE]` calls are true if the current `LOG_TAG` is enabled at the specified priority.
+`LOG_ALWAYS_FATAL` is used to `ALOG` a message, then kill the process. `LOG_FATAL` call is a
+variant of `LOG_ALWAYS_FATAL`, only enabled in engineering, and not release builds. `ALOG_ASSERT`
+is used to `ALOG` a message if the condition is false; the condition is part of the logged message.
+`LOG_EVENT_(INT|LONG)` is used to drop binary content into the Events sub-log.
+
+The log reading interfaces permit opening the logs either singly or multiply, retrieving a log entry
+at a time in time sorted order, optionally limited to a specific pid and tail of the log(s) and
+finally a call closing the logs. A single log can be opened with `android_logger_list_open()`; or
+multiple logs can be opened with `android_logger_list_alloc()`, calling in turn the
+`android_logger_open()` for each log id. Each entry can be retrieved with
+`android_logger_list_read()`. The log(s) can be closed with `android_logger_list_free()`. The logs
+should be opened with an `ANDROID_LOG_RDONLY` mode. `ANDROID_LOG_NONBLOCK` mode will report when
+the log reading is done with an `EAGAIN` error return code, otherwise the
+`android_logger_list_read()` call will block for new entries.
+
+The `ANDROID_LOG_WRAP` mode flag to the `android_logger_list_alloc_time()` signals logd to quiesce
+the reader until the buffer is about to prune at the start time then proceed to dumping content.
+
+The `ANDROID_LOG_PSTORE` mode flag to the `android_logger_open()` is used to switch from the active
+logs to the persistent logs from before the last reboot.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_clear()` function to empty the sub-log. It is recommended to only open log
+`ANDROID_LOG_WRONLY` in that case.
+
+The value returned by `android_logger_open()` can be used as a parameter to the
+`android_logger_get_log_(size|readable_size|version)` to retrieve the sub-log maximum size, readable
+size and log buffer format protocol version respectively. `android_logger_get_id()` returns the id
+that was used when opening the sub-log. It is recommended to open the log `ANDROID_LOG_RDONLY` in
+these cases.
+
+`android_set_log_transport()` selects transport filters. Argument is either `LOGGER_DEFAULT`,
+`LOGGER_LOGD`, or `LOGGER_NULL`. Log to logger daemon for default or logd, or drop contents on floor
+respectively. `Both android_set_log_transport()` and `android_get_log_transport()` return the
+current transport mask, or a negative errno for any problems.
+
+Errors
+------
+
+If messages fail, a negative error code will be returned to the caller.
+
+The `-ENOTCONN` return code indicates that the logger daemon is stopped.
+
+The `-EBADF` return code indicates that the log access point can not be opened, or the log buffer id
+is out of range.
+
+For the `-EAGAIN` return code, this means that the logging message was temporarily backed-up either
+because of Denial Of Service (DOS) logging pressure from some chatty application or service in the
+Android system, or if too small of a value is set in /proc/sys/net/unix/max_dgram_qlen. To aid in
+diagnosing the occurence of this, a binary event from liblog will be sent to the log daemon once a
+new message can get through indicating how many messages were dropped as a result. Please take
+action to resolve the structural problems at the source.
+
+It is generally not advised for the caller to retry the `-EAGAIN` return code as this will only make
+the problem(s) worse and cause your application to temporarily drop to the logger daemon priority,
+BATCH scheduling policy and background task cgroup. If you require a group of messages to be passed
+atomically, merge them into one message with embedded newlines to the maximum length
+`LOGGER_ENTRY_MAX_PAYLOAD`.
+
+Other return codes from writing operation can be returned. Since the library retries on `EINTR`,
+`-EINTR` should never be returned.
diff --git a/liblog/config_read.c b/liblog/config_read.cpp
similarity index 64%
rename from liblog/config_read.c
rename to liblog/config_read.cpp
index ca80c80..3139f78 100644
--- a/liblog/config_read.c
+++ b/liblog/config_read.cpp
@@ -19,23 +19,21 @@
#include "config_read.h"
#include "logger.h"
-LIBLOG_HIDDEN struct listnode __android_log_transport_read = {
- &__android_log_transport_read, &__android_log_transport_read
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_read = {
- &__android_log_persist_read, &__android_log_persist_read
-};
+struct listnode __android_log_transport_read = {&__android_log_transport_read,
+ &__android_log_transport_read};
+struct listnode __android_log_persist_read = {&__android_log_persist_read,
+ &__android_log_persist_read};
-static void __android_log_add_transport(
- struct listnode* list, struct android_log_transport_read* transport) {
- size_t i;
+static void __android_log_add_transport(struct listnode* list,
+ struct android_log_transport_read* transport) {
+ uint32_t i;
/* Try to keep one functioning transport for each log buffer id */
for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
struct android_log_transport_read* transp;
if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(i) >= 0)) {
+ if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
list_add_tail(list, &transport->node);
return;
}
@@ -44,8 +42,8 @@
if (!transp->available) {
return;
}
- if (((*transp->available)(i) < 0) &&
- (!transport->available || ((*transport->available)(i) >= 0))) {
+ if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+ (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
list_add_tail(list, &transport->node);
return;
}
@@ -54,16 +52,9 @@
}
}
-LIBLOG_HIDDEN void __android_log_config_read() {
- if (__android_log_transport & LOGGER_LOCAL) {
- extern struct android_log_transport_read localLoggerRead;
-
- __android_log_add_transport(&__android_log_transport_read, &localLoggerRead);
- }
-
+void __android_log_config_read() {
#if (FAKE_LOG_DEVICE == 0)
- if ((__android_log_transport == LOGGER_DEFAULT) ||
- (__android_log_transport & LOGGER_LOGD)) {
+ if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
extern struct android_log_transport_read logdLoggerRead;
extern struct android_log_transport_read pmsgLoggerRead;
@@ -73,7 +64,7 @@
#endif
}
-LIBLOG_HIDDEN void __android_log_config_read_close() {
+void __android_log_config_read_close() {
struct android_log_transport_read* transport;
struct listnode* n;
diff --git a/liblog/config_read.h b/liblog/config_read.h
index 7b29fa4..212b8a0 100644
--- a/liblog/config_read.h
+++ b/liblog/config_read.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_CONFIG_READ_H__
-#define _LIBLOG_CONFIG_READ_H__
+#pragma once
#include <cutils/list.h>
@@ -23,8 +22,8 @@
__BEGIN_DECLS
-extern LIBLOG_HIDDEN struct listnode __android_log_transport_read;
-extern LIBLOG_HIDDEN struct listnode __android_log_persist_read;
+extern struct listnode __android_log_transport_read;
+extern struct listnode __android_log_persist_read;
#define read_transport_for_each(transp, transports) \
for ((transp) = node_to_item((transports)->next, \
@@ -47,9 +46,7 @@
(transp) = node_to_item((n), struct android_log_transport_read, node), \
(n) = (transp)->node.next)
-LIBLOG_HIDDEN void __android_log_config_read();
-LIBLOG_HIDDEN void __android_log_config_read_close();
+void __android_log_config_read();
+void __android_log_config_read_close();
__END_DECLS
-
-#endif /* _LIBLOG_CONFIG_READ_H__ */
diff --git a/liblog/config_write.c b/liblog/config_write.cpp
similarity index 64%
rename from liblog/config_write.c
rename to liblog/config_write.cpp
index 0a8b52f..d454379 100644
--- a/liblog/config_write.c
+++ b/liblog/config_write.cpp
@@ -19,23 +19,21 @@
#include "config_write.h"
#include "logger.h"
-LIBLOG_HIDDEN struct listnode __android_log_transport_write = {
- &__android_log_transport_write, &__android_log_transport_write
-};
-LIBLOG_HIDDEN struct listnode __android_log_persist_write = {
- &__android_log_persist_write, &__android_log_persist_write
-};
+struct listnode __android_log_transport_write = {&__android_log_transport_write,
+ &__android_log_transport_write};
+struct listnode __android_log_persist_write = {&__android_log_persist_write,
+ &__android_log_persist_write};
-static void __android_log_add_transport(
- struct listnode* list, struct android_log_transport_write* transport) {
- size_t i;
+static void __android_log_add_transport(struct listnode* list,
+ struct android_log_transport_write* transport) {
+ uint32_t i;
/* Try to keep one functioning transport for each log buffer id */
for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
struct android_log_transport_write* transp;
if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(i) >= 0)) {
+ if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
list_add_tail(list, &transport->node);
return;
}
@@ -44,8 +42,8 @@
if (!transp->available) {
return;
}
- if (((*transp->available)(i) < 0) &&
- (!transport->available || ((*transport->available)(i) >= 0))) {
+ if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
+ (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
list_add_tail(list, &transport->node);
return;
}
@@ -54,28 +52,18 @@
}
}
-LIBLOG_HIDDEN void __android_log_config_write() {
- if (__android_log_transport & LOGGER_LOCAL) {
- extern struct android_log_transport_write localLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write,
- &localLoggerWrite);
- }
-
- if ((__android_log_transport == LOGGER_DEFAULT) ||
- (__android_log_transport & LOGGER_LOGD)) {
+void __android_log_config_write() {
+ if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
#if (FAKE_LOG_DEVICE == 0)
extern struct android_log_transport_write logdLoggerWrite;
extern struct android_log_transport_write pmsgLoggerWrite;
- __android_log_add_transport(&__android_log_transport_write,
- &logdLoggerWrite);
+ __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
__android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
#else
extern struct android_log_transport_write fakeLoggerWrite;
- __android_log_add_transport(&__android_log_transport_write,
- &fakeLoggerWrite);
+ __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
#endif
}
@@ -88,8 +76,7 @@
* Remember we can be called here if we are already initialized.
*/
if (list_empty(&__android_log_transport_write)) {
- __android_log_add_transport(&__android_log_transport_write,
- &stderrLoggerWrite);
+ __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite);
} else {
struct android_log_transport_write* transp;
write_transport_for_each(transp, &__android_log_transport_write) {
@@ -97,13 +84,12 @@
return;
}
}
- __android_log_add_transport(&__android_log_persist_write,
- &stderrLoggerWrite);
+ __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite);
}
}
}
-LIBLOG_HIDDEN void __android_log_config_write_close() {
+void __android_log_config_write_close() {
struct android_log_transport_write* transport;
struct listnode* n;
diff --git a/liblog/config_write.h b/liblog/config_write.h
index db1a083..a901f13 100644
--- a/liblog/config_write.h
+++ b/liblog/config_write.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_CONFIG_WRITE_H__
-#define _LIBLOG_CONFIG_WRITE_H__
+#pragma once
#include <cutils/list.h>
@@ -23,8 +22,8 @@
__BEGIN_DECLS
-extern LIBLOG_HIDDEN struct listnode __android_log_transport_write;
-extern LIBLOG_HIDDEN struct listnode __android_log_persist_write;
+extern struct listnode __android_log_transport_write;
+extern struct listnode __android_log_persist_write;
#define write_transport_for_each(transp, transports) \
for ((transp) = node_to_item((transports)->next, \
@@ -47,9 +46,7 @@
(transp) = node_to_item((n), struct android_log_transport_write, node), \
(n) = (transp)->node.next)
-LIBLOG_HIDDEN void __android_log_config_write();
-LIBLOG_HIDDEN void __android_log_config_write_close();
+void __android_log_config_write();
+void __android_log_config_write_close();
__END_DECLS
-
-#endif /* _LIBLOG_CONFIG_WRITE_H__ */
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 574a386..22cf43b 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -407,7 +407,7 @@
//
// We create a private mapping because we want to terminate the log tag
// strings with '\0'.
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+EventTagMap* android_openEventTagMap(const char* fileName) {
EventTagMap* newTagMap;
off_t end[NUM_MAPS];
int save_errno, fd[NUM_MAPS];
@@ -488,7 +488,7 @@
}
// Close the map.
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+void android_closeEventTagMap(EventTagMap* map) {
if (map) delete map;
}
@@ -535,9 +535,7 @@
}
// Look up an entry in the map.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
- size_t* len,
- unsigned int tag) {
+const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
if (!str) {
@@ -549,8 +547,7 @@
}
// Look up an entry in the map.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
- const EventTagMap* map, size_t* len, unsigned int tag) {
+const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
if (!str) {
@@ -565,8 +562,7 @@
// since it will cause the map to change from Shared and backed by a file,
// to Private Dirty and backed up by swap, albeit highly compressible. By
// deprecating this function everywhere, we save 100s of MB of memory space.
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
- unsigned int tag) {
+const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
size_t len;
const char* tagStr = android_lookupEventTag_len(map, &len, tag);
@@ -578,9 +574,7 @@
}
// Look up tagname, generate one if necessary, and return a tag
-LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
- const char* tagname,
- const char* format, int prio) {
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
const char* ep = endOfTag(tagname);
size_t len = ep - tagname;
if (!len || *ep) {
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.cpp
similarity index 87%
rename from liblog/fake_log_device.c
rename to liblog/fake_log_device.cpp
index 1483c24..428a482 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.cpp
@@ -17,6 +17,9 @@
* Intercepts log messages intended for the Android log device.
* Messages are printed to stderr.
*/
+
+#include "fake_log_device.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -30,9 +33,7 @@
#include <time.h>
#include <android/log.h>
-#include <log/uio.h>
-#include "fake_log_device.h"
#include "log_portability.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -181,8 +182,7 @@
logState->debugName[sizeof(logState->debugName) - 1] = '\0';
/* identify binary logs */
- if (!strcmp(pathName + kDevLogLen, "events") ||
- !strcmp(pathName + kDevLogLen, "security")) {
+ if (!strcmp(pathName + kDevLogLen, "events") || !strcmp(pathName + kDevLogLen, "security")) {
logState->isBinary = 1;
}
@@ -204,8 +204,7 @@
while (isspace(*tags)) tags++;
i = 0;
- while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
- i < kMaxTagLen) {
+ while (*tags != '\0' && !isspace(*tags) && *tags != ':' && i < kMaxTagLen) {
tagName[i++] = *tags++;
}
if (i == kMaxTagLen) {
@@ -313,13 +312,11 @@
*/
static const char* getPriorityString(int priority) {
/* the first character of each string should be unique */
- static const char* priorityStrings[] = { "Verbose", "Debug", "Info",
- "Warn", "Error", "Assert" };
+ static const char* priorityStrings[] = {"Verbose", "Debug", "Info", "Warn", "Error", "Assert"};
int idx;
idx = (int)priority - (int)ANDROID_LOG_VERBOSE;
- if (idx < 0 ||
- idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
+ if (idx < 0 || idx >= (int)(sizeof(priorityStrings) / sizeof(priorityStrings[0])))
return "?unknown?";
return priorityStrings[idx];
}
@@ -351,8 +348,7 @@
*
* Log format parsing taken from the long-dead utils/Log.cpp.
*/
-static void showLog(LogState* state, int logPrio, const char* tag,
- const char* msg) {
+static void showLog(LogState* state, int logPrio, const char* tag, const char* msg) {
#if !defined(_WIN32)
struct tm tmBuf;
#endif
@@ -397,19 +393,16 @@
switch (state->outputFormat) {
case FORMAT_TAG:
- prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s: ", priChar, tag);
strcpy(suffixBuf, "\n");
suffixLen = 1;
break;
case FORMAT_PROCESS:
- prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d) ", priChar, pid);
suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), " (%s)\n", tag);
break;
case FORMAT_THREAD:
- prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ",
- priChar, pid, tid);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c(%5d:%5d) ", priChar, pid, tid);
strcpy(suffixBuf, "\n");
suffixLen = 1;
break;
@@ -420,28 +413,24 @@
suffixLen = 1;
break;
case FORMAT_TIME:
- prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %-8s\n\t", timeBuf, tag);
strcpy(suffixBuf, "\n");
suffixLen = 1;
break;
case FORMAT_THREADTIME:
- prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t",
- timeBuf, pid, tid, priChar, tag);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s %5d %5d %c %-8s \n\t", timeBuf, pid,
+ tid, priChar, tag);
strcpy(suffixBuf, "\n");
suffixLen = 1;
break;
case FORMAT_LONG:
- prefixLen =
- snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n",
- timeBuf, pid, tid, priChar, tag);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "[ %s %5d:%5d %c/%-8s ]\n", timeBuf, pid,
+ tid, priChar, tag);
strcpy(suffixBuf, "\n\n");
suffixLen = 2;
break;
default:
- prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
- "%c/%-8s(%5d): ", priChar, tag, pid);
+ prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%c/%-8s(%5d): ", priChar, tag, pid);
strcpy(suffixBuf, "\n");
suffixLen = 1;
break;
@@ -559,8 +548,7 @@
* tag (N bytes -- null-terminated ASCII string)
* message (N bytes -- null-terminated ASCII string)
*/
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
- int count) {
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) {
LogState* state;
/* Make sure that no-one frees the LogState while we're using it.
@@ -572,17 +560,24 @@
state = fdToLogState(fd);
if (state == NULL) {
errno = EBADF;
- goto error;
+ unlock();
+ return -1;
}
if (state->isBinary) {
TRACE("%s: ignoring binary log\n", state->debugName);
- goto bail;
+ unlock();
+ int len = 0;
+ for (int i = 0; i < count; ++i) {
+ len += vector[i].iov_len;
+ }
+ return len;
}
if (count != 3) {
TRACE("%s: writevLog with count=%d not expected\n", state->debugName, count);
- goto error;
+ unlock();
+ return -1;
}
/* pull out the three fields */
@@ -598,7 +593,6 @@
break; /* reached end of configured values */
if (strcmp(state->tagSet[i].tag, tag) == 0) {
- // TRACE("MATCH tag '%s'\n", tag);
minPrio = state->tagSet[i].minPriority;
break;
}
@@ -606,21 +600,14 @@
if (logPrio >= minPrio) {
showLog(state, logPrio, tag, msg);
- } else {
- // TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
}
-bail:
unlock();
int len = 0;
for (i = 0; i < count; ++i) {
len += vector[i].iov_len;
}
return len;
-
-error:
- unlock();
- return -1;
}
/*
@@ -636,7 +623,7 @@
* call is in the exit handler. Logging can continue in the exit handler to
* help debug HOST tools ...
*/
-LIBLOG_HIDDEN int fakeLogClose(int fd) {
+int fakeLogClose(int fd) {
deleteFakeFd(fd);
return 0;
}
@@ -644,7 +631,7 @@
/*
* Open a log output device and return a fake fd.
*/
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName) {
+int fakeLogOpen(const char* pathName) {
LogState* logState;
int fd = -1;
@@ -663,26 +650,20 @@
return fd;
}
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf __unused,
- size_t buf_size __unused) {
+ssize_t __send_log_msg(char*, size_t) {
return -ENODEV;
}
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
- const char* tag __unused,
- int def) {
+int __android_log_is_loggable(int prio, const char*, int def) {
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
}
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio,
- const char* tag __unused,
- size_t len __unused,
- int def) {
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def) {
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
}
-LIBLOG_ABI_PRIVATE int __android_log_is_debuggable() {
+int __android_log_is_debuggable() {
return 1;
}
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 7b0e745..ce54db2 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -14,18 +14,24 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_FAKE_LOG_DEVICE_H
-#define _LIBLOG_FAKE_LOG_DEVICE_H
+#pragma once
#include <sys/types.h>
#include "log_portability.h"
+#include "uio.h"
struct iovec;
-LIBLOG_HIDDEN int fakeLogOpen(const char* pathName);
-LIBLOG_HIDDEN int fakeLogClose(int fd);
-LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd, const struct iovec* vector,
- int count);
+__BEGIN_DECLS
-#endif // _LIBLOG_FAKE_LOG_DEVICE_H
+int fakeLogOpen(const char* pathName);
+int fakeLogClose(int fd);
+ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+
+ssize_t __send_log_msg(char*, size_t);
+int __android_log_is_loggable(int prio, const char*, int def);
+int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
+int __android_log_is_debuggable();
+
+__END_DECLS
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.cpp
similarity index 80%
rename from liblog/fake_writer.c
rename to liblog/fake_writer.cpp
index 403dc72..c0b0e69 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.cpp
@@ -27,19 +27,18 @@
static int fakeOpen();
static void fakeClose();
-static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec,
- size_t nr);
+static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
-static int logFds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1, -1 };
+static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
-LIBLOG_HIDDEN struct android_log_transport_write fakeLoggerWrite = {
- .node = { &fakeLoggerWrite.node, &fakeLoggerWrite.node },
- .context.priv = &logFds,
- .name = "fake",
- .available = NULL,
- .open = fakeOpen,
- .close = fakeClose,
- .write = fakeWrite,
+struct android_log_transport_write fakeLoggerWrite = {
+ .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
+ .context.priv = &logFds,
+ .name = "fake",
+ .available = NULL,
+ .open = fakeOpen,
+ .close = fakeClose,
+ .write = fakeWrite,
};
static int fakeOpen() {
@@ -54,7 +53,7 @@
if (logFds[i] >= 0) {
continue;
}
- snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
+ snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(static_cast<log_id_t>(i)));
logFds[i] = fakeLogOpen(buf);
if (logFds[i] < 0) {
fprintf(stderr, "fakeLogOpen(%s) failed\n", buf);
@@ -72,8 +71,7 @@
}
}
-static int fakeWrite(log_id_t log_id, struct timespec* ts __unused,
- struct iovec* vec, size_t nr) {
+static int fakeWrite(log_id_t log_id, struct timespec*, struct iovec* vec, size_t nr) {
ssize_t ret;
size_t i;
int logFd, len;
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index ee9220d..935590d 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -14,25 +14,7 @@
* limitations under the License.
*/
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
-
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- * This file is part of Android's set of stable system headers
- * exposed by the Android NDK (Native Development Kit) since
- * platform release 1.5
- *
- * Third-party source AND binary code relies on the definitions
- * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
+#pragma once
/**
* @addtogroup Logging
@@ -140,9 +122,10 @@
*
* Most callers should use
* [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
- * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
- * bionic if more control is needed. They support automatically including the
- * source filename and line number more conveniently than this function.
+ * `<assert.h>` instead, or the `__assert` and `__assert2` functions
+ * provided by bionic if more control is needed. They support automatically
+ * including the source filename and line number more conveniently than this
+ * function.
*/
void __android_log_assert(const char* cond, const char* tag, const char* fmt,
...)
@@ -154,27 +137,51 @@
#ifndef log_id_t_defined
#define log_id_t_defined
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
typedef enum log_id {
LOG_ID_MIN = 0,
+ /** The main log buffer. This is the only log buffer available to apps. */
LOG_ID_MAIN = 0,
+ /** The radio log buffer. */
LOG_ID_RADIO = 1,
+ /** The event log buffer. */
LOG_ID_EVENTS = 2,
+ /** The system log buffer. */
LOG_ID_SYSTEM = 3,
+ /** The crash log buffer. */
LOG_ID_CRASH = 4,
+ /** The statistics log buffer. */
LOG_ID_STATS = 5,
+ /** The security log buffer. */
LOG_ID_SECURITY = 6,
- LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+ /** The kernel log buffer. */
+ LOG_ID_KERNEL = 7,
LOG_ID_MAX
} log_id_t;
#endif
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
*/
int __android_log_buf_write(int bufID, int prio, const char* tag,
const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
int __android_log_buf_print(int bufID, int prio, const char* tag,
const char* fmt, ...)
#if defined(__GNUC__)
@@ -187,5 +194,3 @@
#endif
/** @} */
-
-#endif /* _ANDROID_LOG_H */
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 8dd9157..2687b3a 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
-#define _LIBS_CUTILS_EVENTTAGMAP_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -69,5 +68,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3813e6e..5928649 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_H
-#define _LIBS_LOG_LOG_H
+#pragma once
/* Too many in the ecosystem assume these are included */
#if !defined(_WIN32)
@@ -35,7 +34,6 @@
#include <log/log_safetynet.h>
#include <log/log_system.h>
#include <log/log_time.h>
-#include <log/uio.h> /* helper to define iovec for portability */
#ifdef __cplusplus
extern "C" {
@@ -152,35 +150,12 @@
#ifdef __linux__
-#ifndef __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOCK_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
clockid_t android_log_clockid(void);
-#endif
#endif /* __linux__ */
/* --------------------------------------------------------------------- */
-#ifndef __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#elif __ANDROID_API__ > 18 /* > JellyBean */
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_CLOSE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_CLOSE_INTERFACE
/*
* Release any logger resources (a new log write will immediately re-acquire)
*
@@ -188,54 +163,6 @@
* all O_CLOEXEC so wil self clean on exec().
*/
void __android_log_close(void);
-#endif
-
-#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#elif __ANDROID_API__ > 25 /* > OC */
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
-
-/*
- * if last is NULL, caller _must_ provide a consistent value for seconds.
- *
- * Return -1 if we can not acquire a lock, which below will permit the logging,
- * error on allowing a log message through.
- */
-int __android_log_ratelimit(time_t seconds, time_t* last);
-
-/*
- * Usage:
- *
- * // Global default and state
- * IF_ALOG_RATELIMIT() {
- * ALOG*(...);
- * }
- *
- * // local state, 10 seconds ratelimit
- * static time_t local_state;
- * IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
- * ALOG*(...);
- * }
- */
-
-#define IF_ALOG_RATELIMIT() if (__android_log_ratelimit(0, NULL) > 0)
-#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
- if (__android_log_ratelimit(seconds, state) > 0)
-
-#else
-
-/* No ratelimiting as API unsupported */
-#define IF_ALOG_RATELIMIT() if (1)
-#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
-
-#endif
#if defined(__clang__)
#pragma clang diagnostic pop
@@ -244,5 +171,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index 1b7c377..636d417 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_EVENT_LIST_H
-#define _LIBS_LOG_EVENT_LIST_H
+#pragma once
#include <errno.h>
#include <stdint.h>
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
#include <string>
-}
#endif
#include <log/log.h>
@@ -32,18 +29,6 @@
extern "C" {
#endif
-#ifndef __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_EVENT_INTERFACE
-
/* For manipulating lists of events. */
#define ANDROID_MAX_LIST_NEST_DEPTH 8
@@ -124,8 +109,6 @@
/* android_log_list C++ helpers */
extern "C++" {
class android_log_event_list {
- friend class __android_log_event_list;
-
private:
android_log_context ctx;
int ret;
@@ -137,10 +120,6 @@
explicit android_log_event_list(int tag) : ret(0) {
ctx = create_android_logger(static_cast<uint32_t>(tag));
}
- explicit android_log_event_list(log_msg& log_msg) : ret(0) {
- ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
- log_msg.entry.len - sizeof(uint32_t));
- }
~android_log_event_list() {
android_log_destroy(&ctx);
}
@@ -208,14 +187,12 @@
return *this;
}
-#if defined(_USING_LIBCXX)
android_log_event_list& operator<<(const std::string& value) {
int retval =
android_log_write_string8_len(ctx, value.data(), value.length());
if (retval < 0) ret = retval;
return *this;
}
-#endif
android_log_event_list& operator<<(float value) {
int retval = android_log_write_float32(ctx, value);
@@ -269,7 +246,6 @@
return ret >= 0;
}
-#if defined(_USING_LIBCXX)
bool AppendString(const std::string& value) {
int retval =
android_log_write_string8_len(ctx, value.data(), value.length());
@@ -283,7 +259,6 @@
if (retval < 0) ret = retval;
return ret;
}
-#endif
bool AppendFloat(float value) {
int retval = android_log_write_float32(ctx, value);
@@ -302,22 +277,11 @@
if (retval < 0) ret = retval;
return ret >= 0;
}
-
- android_log_list_element read() {
- return android_log_read_next(ctx);
- }
- android_log_list_element peek() {
- return android_log_peek_next(ctx);
- }
};
}
#endif
#endif
-#endif /* __ANDROID_USE_LIBLOG_EVENT_INTERFACE */
-
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_EVENT_LIST_H */
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c44f5a2..c052a50 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_ID_H
-#define _LIBS_LOG_LOG_ID_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -62,5 +61,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_LOG_ID_H */
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 53653de..64791c2 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_MAIN_H
-#define _LIBS_LOG_LOG_MAIN_H
+#pragma once
#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
#include <android/log.h>
-#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -349,20 +349,6 @@
* over Android.
*/
-#ifndef __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 2
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE
-
/*
* Use the per-tag properties "log.tag.<tagname>" to generate a runtime
* result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
@@ -370,12 +356,7 @@
* any other value.
*/
int __android_log_is_loggable(int prio, const char* tag, int default_prio);
-
-#if __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE > 1
-#include <sys/types.h>
-
-int __android_log_is_loggable_len(int prio, const char* tag, size_t len,
- int default_prio);
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio);
#if LOG_NDEBUG /* Production */
#define android_testLog(prio, tag) \
@@ -387,28 +368,8 @@
ANDROID_LOG_VERBOSE) != 0)
#endif
-#else
-
-#if LOG_NDEBUG /* Production */
-#define android_testLog(prio, tag) \
- (__android_log_is_loggable(prio, tag, ANDROID_LOG_DEBUG) != 0)
-#else
-#define android_testLog(prio, tag) \
- (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
-#endif
-
-#endif
-
-#else /* __ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
-#define android_testLog(prio, tag) (1)
-
-#endif /* !__ANDROID_USE_LIBLOG_LOGGABLE_INTERFACE */
-
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
__END_DECLS
-
-#endif /* _LIBS_LOG_LOG_MAIN_H */
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 7d398a6..3a8af6d 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -7,29 +7,14 @@
** General Public License.
*/
-#ifndef _LIBS_LOG_PROPERTIES_H
-#define _LIBS_LOG_PROPERTIES_H
+#pragma once
#ifdef __cplusplus
extern "C" {
#endif
-#ifndef __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#elif __ANDROID_API__ > 24 /* > Nougat */
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_IS_DEBUGGABLE_INTERFACE
int __android_log_is_debuggable();
-#endif
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_PROPERTIES_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index bd629fe..8b8a362 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_RADIO_H
-#define _LIBS_LOG_LOG_RADIO_H
+#pragma once
#include <android/log.h>
#include <log/log_id.h>
@@ -140,5 +139,3 @@
LOG_TAG, __VA_ARGS__)) \
: (void)0)
#endif
-
-#endif /* _LIBS_LOG_LOG_RADIO_H */
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 93b9d4e..fdef306 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_READ_H
-#define _LIBS_LOG_LOG_READ_H
+#pragma once
+
+#include <sys/types.h>
/* deal with possible sys/cdefs.h conflict with fcntl.h */
#ifdef __unused
@@ -47,6 +48,8 @@
* access to raw information, or parsing is an issue.
*/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
/*
* The userspace structure for version 1 of the logger_entry ABI.
*/
@@ -59,9 +62,7 @@
int32_t tid; /* generating process's tid */
int32_t sec; /* seconds since Epoch */
int32_t nsec; /* nanoseconds */
-#ifndef __cplusplus
char msg[0]; /* the entry's payload */
-#endif
};
#endif
@@ -78,9 +79,7 @@
int32_t sec; /* seconds since Epoch */
int32_t nsec; /* nanoseconds */
uint32_t euid; /* effective UID of logger */
-#ifndef __cplusplus
char msg[0]; /* the entry's payload */
-#endif
} __attribute__((__packed__));
#endif
@@ -97,9 +96,7 @@
int32_t sec; /* seconds since Epoch */
int32_t nsec; /* nanoseconds */
uint32_t lid; /* log id of the payload */
-#ifndef __cplusplus
char msg[0]; /* the entry's payload */
-#endif
} __attribute__((__packed__));
#endif
@@ -117,11 +114,10 @@
uint32_t nsec; /* nanoseconds */
uint32_t lid; /* log id of the payload, bottom 4 bits currently */
uint32_t uid; /* generating process's uid */
-#ifndef __cplusplus
char msg[0]; /* the entry's payload */
-#endif
};
#endif
+#pragma clang diagnostic pop
/*
* The maximum size of the log entry payload that can be
@@ -197,22 +193,6 @@
};
#endif
-#ifndef __ANDROID_USE_LIBLOG_READER_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 23 /* > Marshmallow */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 3
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 2
-#elif __ANDROID_API__ > 19 /* > KitKat */
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_READER_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE
-
struct logger;
log_id_t android_logger_get_id(struct logger* logger);
@@ -225,14 +205,12 @@
struct logger_list;
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
ssize_t android_logger_get_statistics(struct logger_list* logger_list,
char* buf, size_t len);
ssize_t android_logger_get_prune_list(struct logger_list* logger_list,
char* buf, size_t len);
int android_logger_set_prune_list(struct logger_list* logger_list, char* buf,
size_t len);
-#endif
#define ANDROID_LOG_RDONLY O_RDONLY
#define ANDROID_LOG_WRONLY O_WRONLY
@@ -243,13 +221,9 @@
#else
#define ANDROID_LOG_NONBLOCK O_NONBLOCK
#endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
-#endif
-#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 1
#define ANDROID_LOG_PSTORE 0x80000000
-#endif
struct logger_list* android_logger_list_alloc(int mode, unsigned int tail,
pid_t pid);
@@ -268,10 +242,6 @@
unsigned int tail, pid_t pid);
#define android_logger_list_close android_logger_list_free
-#endif /* __ANDROID_USE_LIBLOG_READER_INTERFACE */
-
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_LOG_H */
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index 07e8c8a..d3e9b19 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -7,8 +7,7 @@
** General Public License.
*/
-#ifndef _LIBS_LOG_SAFETYNET_H
-#define _LIBS_LOG_SAFETYNET_H
+#pragma once
#include <stdint.h>
@@ -16,18 +15,6 @@
extern "C" {
#endif
-#ifndef _ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-#ifndef __ANDROID_API__
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#elif __ANDROID_API__ > 22 /* > Lollipop */
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 1
-#else
-#define __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE 0
-#endif
-#endif
-
-#if __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE
-
#define android_errorWriteLog(tag, subTag) \
__android_log_error_write(tag, subTag, -1, NULL, 0)
@@ -37,10 +24,6 @@
int __android_log_error_write(int tag, const char* subTag, int32_t uid,
const char* data, uint32_t dataLen);
-#endif /* __ANDROID_USE_LIBLOG_SAFETYNET_INTERFACE */
-
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_SAFETYNET_H */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index 3b5ae22..eaec741 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_SYSTEM_H
-#define _LIBS_LOG_LOG_SYSTEM_H
+#pragma once
#include <android/log.h>
#include <log/log_id.h>
@@ -138,5 +137,3 @@
LOG_TAG, __VA_ARGS__)) \
: (void)0)
#endif
-
-#endif /* _LIBS_LOG_LOG_SYSTEM_H */
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 309f5d1..09c9910 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBS_LOG_LOG_TIME_H
-#define _LIBS_LOG_LOG_TIME_H
+#pragma once
#include <stdint.h>
#include <time.h>
@@ -34,6 +33,8 @@
#ifdef __cplusplus
+extern "C" {
+
/*
* NB: we did NOT define a copy constructor. This will result in structure
* no longer being compatible with pass-by-value which is desired
@@ -41,25 +42,19 @@
*/
struct log_time {
public:
- uint32_t tv_sec; /* good to Feb 5 2106 */
- uint32_t tv_nsec;
+ uint32_t tv_sec = 0; /* good to Feb 5 2106 */
+ uint32_t tv_nsec = 0;
static const uint32_t tv_sec_max = 0xFFFFFFFFUL;
static const uint32_t tv_nsec_max = 999999999UL;
+ static const timespec EPOCH;
- log_time(const timespec& T)
- : tv_sec(static_cast<uint32_t>(T.tv_sec)),
- tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {
- }
+ log_time() {}
+ explicit log_time(const timespec& T)
+ : tv_sec(static_cast<uint32_t>(T.tv_sec)), tv_nsec(static_cast<uint32_t>(T.tv_nsec)) {}
explicit log_time(uint32_t sec, uint32_t nsec = 0)
: tv_sec(sec), tv_nsec(nsec) {
}
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define __struct_log_time_private_defined
- static const timespec EPOCH;
-#endif
- log_time() {
- }
#ifdef __linux__
explicit log_time(clockid_t id) {
timespec T;
@@ -103,7 +98,6 @@
return !(*this > T);
}
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
log_time operator-=(const timespec& T);
log_time operator-(const timespec& T) const {
log_time local(*this);
@@ -114,7 +108,6 @@
log_time local(*this);
return local += T;
}
-#endif
/* log_time */
bool operator==(const log_time& T) const {
@@ -138,7 +131,6 @@
return !(*this > T);
}
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
log_time operator-=(const log_time& T);
log_time operator-(const log_time& T) const {
log_time local(*this);
@@ -149,7 +141,6 @@
log_time local(*this);
return local += T;
}
-#endif
uint64_t nsec() const {
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
@@ -163,13 +154,12 @@
tv_nsec / (NS_PER_SEC / MS_PER_SEC);
}
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
static const char default_format[];
/* Add %#q for the fraction of a second to the standard library functions */
char* strptime(const char* s, const char* format = default_format);
-#endif
} __attribute__((__packed__));
+}
#else /* __cplusplus */
@@ -181,5 +171,3 @@
#endif /* __cplusplus */
#endif /* __struct_log_time_defined */
-
-#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
index 80b30db..b48761a 100644
--- a/liblog/include/log/log_transport.h
+++ b/liblog/include/log/log_transport.h
@@ -7,8 +7,7 @@
** General Public License.
*/
-#ifndef _LIBS_LOG_TRANSPORT_H
-#define _LIBS_LOG_TRANSPORT_H
+#pragma once
#ifdef __cplusplus
extern "C" {
@@ -22,7 +21,7 @@
#define LOGGER_LOGD 0x01
#define LOGGER_KERNEL 0x02 /* Reserved/Deprecated */
#define LOGGER_NULL 0x04 /* Does not release resources of other selections */
-#define LOGGER_LOCAL 0x08 /* logs sent to local memory */
+#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
#define LOGGER_STDERR 0x10 /* logs sent to stderr */
/* clang-format on */
@@ -33,5 +32,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /* _LIBS_LOG_TRANSPORT_H */
diff --git a/liblog/include/log/logd.h b/liblog/include/log/logd.h
deleted file mode 100644
index 77400ca..0000000
--- a/liblog/include/log/logd.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGD_H
-#define _LIBS_LOG_LOGD_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logd.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGD_H*/
diff --git a/liblog/include/log/logger.h b/liblog/include/log/logger.h
deleted file mode 100644
index 1bf2d17..0000000
--- a/liblog/include/log/logger.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef _LIBS_LOG_LOGGER_H
-#define _LIBS_LOG_LOGGER_H
-#include <log/log.h>
-#warning "Deprecated: do not include log/logger.h, use log/log.h instead"
-#endif /*_LIBS_LOG_LOGGER_H*/
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index ca58bc7..8f4b187 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LOGPRINT_H
-#define _LOGPRINT_H
+#pragma once
#include <pthread.h>
@@ -158,5 +157,3 @@
#ifdef __cplusplus
}
#endif
-
-#endif /*_LOGPRINT_H*/
diff --git a/liblog/include/log/uio.h b/liblog/include/log/uio.h
deleted file mode 100644
index a492bae..0000000
--- a/liblog/include/log/uio.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBS_CUTILS_UIO_H
-#define _LIBS_CUTILS_UIO_H
-
-#if !defined(_WIN32)
-
-#include <sys/uio.h>
-
-#else
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Implementation of sys/uio.h for Win32.
-//
-
-#include <stddef.h>
-
-struct iovec {
- void* iov_base;
- size_t iov_len;
-};
-
-extern int readv(int fd, struct iovec* vecs, int count);
-extern int writev(int fd, const struct iovec* vecs, int count);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
-#endif /* _LIBS_UTILS_UIO_H */
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index b927b46..5e04148 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -16,8 +16,7 @@
/* This file is used to define the internal protocol for the Android Logger */
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
+#pragma once
/* Android private interfaces */
@@ -25,10 +24,8 @@
#include <stdint.h>
#include <sys/types.h>
-#if (defined(__cplusplus) && defined(_USING_LIBCXX))
-extern "C++" {
+#ifdef __cplusplus
#include <string>
-}
#endif
#include <log/log.h>
@@ -153,41 +150,6 @@
/* Retrieve the composed event buffer */
int android_log_write_list_buffer(android_log_context ctx, const char** msg);
-#ifdef __cplusplus
-#ifdef __class_android_log_event_list_defined
-#ifndef __class_android_log_event_list_private_defined
-#define __class_android_log_event_list_private_defined
-/* android_log_context C++ helpers */
-extern "C++" {
-class __android_log_event_list : public android_log_event_list {
- __android_log_event_list(const android_log_event_list&) = delete;
- void operator=(const __android_log_event_list&) = delete;
-
- public:
- explicit __android_log_event_list(int tag) : android_log_event_list(tag) {
- }
- explicit __android_log_event_list(log_msg& log_msg)
- : android_log_event_list(log_msg) {
- }
-
-#if defined(_USING_LIBCXX)
- operator std::string() {
- if (ret) return std::string("");
- const char* cp = nullptr;
- ssize_t len = android_log_write_list_buffer(ctx, &cp);
- if (len < 0) ret = len;
- if (!cp || (len <= 0)) return std::string("");
- return std::string(cp, len);
- }
-#endif
-};
-}
-#endif
-#endif
-#endif
-
#if defined(__cplusplus)
}
#endif
-
-#endif /* _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_ */
diff --git a/liblog/include_vndk/log/log_event_list.h b/liblog/include_vndk/log/log_event_list.h
index 9f74534..1f3dd37 100644
--- a/liblog/include_vndk/log/log_event_list.h
+++ b/liblog/include_vndk/log/log_event_list.h
@@ -27,8 +27,6 @@
extern "C" {
#endif
-#define __ANDROID_USE_LIBLOG_EVENT_INTERFACE 1
-
/*
* The opaque context used to manipulate lists of events.
*/
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 015c9cb..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -19,12 +19,12 @@
android_logger_get_log_readable_size; # vndk
android_logger_get_log_version; # vndk
android_logger_get_log_size; # vndk
- android_logger_list_alloc; # vndk
- android_logger_list_alloc_time; # vndk
- android_logger_list_free; # vndk
+ android_logger_list_alloc; # apex vndk
+ android_logger_list_alloc_time; # apex vndk
+ android_logger_list_free; # apex vndk
android_logger_list_open; # vndk
- android_logger_list_read; # vndk
- android_logger_open; # vndk
+ android_logger_list_read; # apex vndk
+ android_logger_open; # apex vndk
android_logger_set_log_size; # vndk
};
@@ -33,18 +33,18 @@
android_logger_get_prune_list; # vndk
android_logger_set_prune_list; # vndk
android_logger_get_statistics; # vndk
- __android_log_error_write; # vndk
+ __android_log_error_write; # apex vndk
__android_log_is_loggable;
- create_android_logger; #vndk
- android_log_destroy; #vndk
- android_log_write_list_begin; #vndk
- android_log_write_list_end; #vndk
- android_log_write_int32; #vndk
- android_log_write_int64; #vndk
- android_log_write_string8; #vndk
- android_log_write_string8_len; #vndk
- android_log_write_float32; #vndk
- android_log_write_list; #vndk
+ create_android_logger; # apex vndk
+ android_log_destroy; # apex vndk
+ android_log_write_list_begin; # apex vndk
+ android_log_write_list_end; # apex vndk
+ android_log_write_int32; # apex vndk
+ android_log_write_int64; # apex vndk
+ android_log_write_string8; # apex vndk
+ android_log_write_string8_len; # apex vndk
+ android_log_write_float32; # apex vndk
+ android_log_write_list; # apex vndk
};
@@ -56,6 +56,27 @@
LIBLOG_Q {
global:
+ __android_log_bswrite; # apex
+ __android_log_btwrite; # apex
+ __android_log_bwrite; # apex
+ __android_log_close; # apex
+ __android_log_security; # apex
android_log_reset; #vndk
android_log_parser_reset; #vndk
};
+
+LIBLOG_PRIVATE {
+ global:
+ __android_log_pmsg_file_read;
+ __android_log_pmsg_file_write;
+ __android_log_security_bswrite;
+ __android_logger_get_buffer_size;
+ __android_logger_property_get_bool;
+ android_openEventTagMap;
+ android_log_processBinaryLogBuffer;
+ android_log_processLogBuffer;
+ android_log_read_next;
+ android_log_write_list_buffer;
+ android_lookupEventTagNum;
+ create_android_log_parser;
+};
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
deleted file mode 100644
index 563cb3f..0000000
--- a/liblog/local_logger.c
+++ /dev/null
@@ -1,550 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#if !defined(__MINGW32__)
-#include <pwd.h>
-#endif
-#include <log/uio.h>
-#include <sched.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <cutils/list.h> /* template, no library dependency */
-#include <log/log_transport.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <system/thread_defs.h>
-
-#include "config_read.h"
-#include "config_write.h"
-#include "log_portability.h"
-#include "logger.h"
-
-static const char baseServiceName[] = "android.logd";
-
-static int writeToLocalInit();
-static int writeToLocalAvailable(log_id_t logId);
-static void writeToLocalReset();
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
- struct iovec* vec, size_t nr);
-
-LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = {
- .node = { &localLoggerWrite.node, &localLoggerWrite.node },
- .context.priv = NULL,
- .name = "local",
- .available = writeToLocalAvailable,
- .open = writeToLocalInit,
- .close = writeToLocalReset,
- .write = writeToLocalWrite,
-};
-
-static int writeToLocalVersion(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg);
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp);
-static void writeToLocalClose(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp);
-static int writeToLocalClear(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t writeToLocalSetSize(
- struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused, size_t size);
-static ssize_t writeToLocalGetReadbleSize(
- struct android_log_logger* logger,
- struct android_log_transport_context* transp);
-
-struct android_log_transport_read localLoggerRead = {
- .node = { &localLoggerRead.node, &localLoggerRead.node },
- .name = "local",
- .available = writeToLocalAvailable,
- .version = writeToLocalVersion,
- .read = writeToLocalRead,
- .poll = writeToLocalPoll,
- .close = writeToLocalClose,
- .clear = writeToLocalClear,
- .getSize = writeToLocalGetSize,
- .setSize = writeToLocalSetSize,
- .getReadableSize = writeToLocalGetReadbleSize,
- .getPrune = NULL,
- .setPrune = NULL,
- .getStats = NULL,
-};
-
-struct LogBufferElement {
- struct listnode node;
- log_id_t logId;
- pid_t tid;
- log_time timestamp;
- unsigned short len;
- char msg[];
-};
-
-static const size_t MAX_SIZE_DEFAULT = 32768;
-
-/*
- * Number of log buffers we support with the following assumption:
- * . . .
- * LOG_ID_SECURITY = 5, // security logs go to the system logs only
- * LOG_ID_KERNEL = 6, // place last, third-parties can not use it
- * LOG_ID_MAX
- * } log_id_t;
- *
- * Confirm the following should <log/log_id.h> be adjusted in the future.
- */
-#define NUMBER_OF_LOG_BUFFERS \
- ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? LOG_ID_SECURITY : LOG_ID_KERNEL)
-#define BLOCK_LOG_BUFFERS(id) \
- (((id) == LOG_ID_SECURITY) || ((id) == LOG_ID_KERNEL))
-
-static struct LogBuffer {
- struct listnode head;
- pthread_rwlock_t listLock;
- char* serviceName; /* Also indicates ready by having a value */
- /* Order and proximity important for memset */
- size_t number[NUMBER_OF_LOG_BUFFERS]; /* clear memset */
- size_t size[NUMBER_OF_LOG_BUFFERS]; /* clear memset */
- size_t totalSize[NUMBER_OF_LOG_BUFFERS]; /* init memset */
- size_t maxSize[NUMBER_OF_LOG_BUFFERS]; /* init MAX_SIZE_DEFAULT */
- struct listnode* last[NUMBER_OF_LOG_BUFFERS]; /* init &head */
-} logbuf = {
- .head = { &logbuf.head, &logbuf.head }, .listLock = PTHREAD_RWLOCK_INITIALIZER,
-};
-
-static void LogBufferInit(struct LogBuffer* log) {
- size_t i;
-
- pthread_rwlock_wrlock(&log->listLock);
- list_init(&log->head);
- memset(log->number, 0,
- sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize));
- for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
- log->maxSize[i] = MAX_SIZE_DEFAULT;
- log->last[i] = &log->head;
- }
-#ifdef __BIONIC__
- asprintf(&log->serviceName, "%s@%d:%d", baseServiceName, __android_log_uid(),
- getpid());
-#else
- char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8];
- snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName,
- __android_log_uid(), getpid());
- log->serviceName = strdup(buffer);
-#endif
- pthread_rwlock_unlock(&log->listLock);
-}
-
-static void LogBufferClear(struct LogBuffer* log) {
- size_t i;
- struct listnode* node;
-
- pthread_rwlock_wrlock(&log->listLock);
- memset(log->number, 0, sizeof(log->number) + sizeof(log->size));
- for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
- log->last[i] = &log->head;
- }
- while ((node = list_head(&log->head)) != &log->head) {
- struct LogBufferElement* element;
-
- element = node_to_item(node, struct LogBufferElement, node);
- list_remove(node);
- free(element);
- }
- pthread_rwlock_unlock(&log->listLock);
-}
-
-static inline void LogBufferFree(struct LogBuffer* log) {
- pthread_rwlock_wrlock(&log->listLock);
- free(log->serviceName);
- log->serviceName = NULL;
- pthread_rwlock_unlock(&log->listLock);
- LogBufferClear(log);
-}
-
-static int LogBufferLog(struct LogBuffer* log,
- struct LogBufferElement* element) {
- log_id_t logId = element->logId;
-
- pthread_rwlock_wrlock(&log->listLock);
- log->number[logId]++;
- log->size[logId] += element->len;
- log->totalSize[logId] += element->len;
- /* prune entry(s) until enough space is available */
- if (log->last[logId] == &log->head) {
- log->last[logId] = list_tail(&log->head);
- }
- while (log->size[logId] > log->maxSize[logId]) {
- struct listnode* node = log->last[logId];
- struct LogBufferElement* e;
- struct android_log_logger_list* logger_list;
-
- e = node_to_item(node, struct LogBufferElement, node);
- log->number[logId]--;
- log->size[logId] -= e->len;
- logger_list_rdlock();
- logger_list_for_each(logger_list) {
- struct android_log_transport_context* transp;
-
- transport_context_for_each(transp, logger_list) {
- if ((transp->transport == &localLoggerRead) &&
- (transp->context.node == node)) {
- if (node == &log->head) {
- transp->context.node = &log->head;
- } else {
- transp->context.node = node->next;
- }
- }
- }
- }
- logger_list_unlock();
- if (node != &log->head) {
- log->last[logId] = node->prev;
- }
- list_remove(node);
- LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
- free(e);
- }
- /* add entry to list */
- list_add_head(&log->head, &element->node);
- /* ToDo: wake up all readers */
- pthread_rwlock_unlock(&log->listLock);
-
- return element->len;
-}
-
-/*
- * return zero if permitted to log directly to logd,
- * return 1 if binder server started and
- * return negative error number if failed to start binder server.
- */
-static int writeToLocalInit() {
- pthread_attr_t attr;
- struct LogBuffer* log;
-
- if (writeToLocalAvailable(LOG_ID_MAIN) < 0) {
- return -EPERM;
- }
-
- log = &logbuf;
- if (!log->serviceName) {
- LogBufferInit(log);
- }
-
- if (!log->serviceName) {
- LogBufferFree(log);
- return -ENOMEM;
- }
-
- return EPERM; /* successful local-only logging */
-}
-
-static void writeToLocalReset() {
- LogBufferFree(&logbuf);
-}
-
-static int writeToLocalAvailable(log_id_t logId) {
-#if !defined(__MINGW32__)
- uid_t uid;
-#endif
-
- if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
- return -EINVAL;
- }
-
-/* Android hard coded permitted, system goes to logd */
-#if !defined(__MINGW32__)
- if (__android_log_transport == LOGGER_DEFAULT) {
- uid = __android_log_uid();
- if ((uid < AID_APP) && (getpwuid(uid) != NULL)) {
- return -EPERM;
- }
- }
-#endif
-
- /* ToDo: Ask package manager for LOGD permissions */
- /* Assume we do _not_ have permissions to go to LOGD, so must go local */
- return 0;
-}
-
-static int writeToLocalWrite(log_id_t logId, struct timespec* ts,
- struct iovec* vec, size_t nr) {
- size_t len, i;
- struct LogBufferElement* element;
-
- if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
- return -EINVAL;
- }
-
- len = 0;
- for (i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
-
- if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
- len = LOGGER_ENTRY_MAX_PAYLOAD;
- }
- element = (struct LogBufferElement*)calloc(
- 1, sizeof(struct LogBufferElement) + len + 1);
- if (!element) {
- return errno ? -errno : -ENOMEM;
- }
- element->timestamp.tv_sec = ts->tv_sec;
- element->timestamp.tv_nsec = ts->tv_nsec;
-#ifdef __BIONIC__
- element->tid = gettid();
-#else
- element->tid = getpid();
-#endif
- element->logId = logId;
- element->len = len;
-
- char* cp = element->msg;
- for (i = 0; i < nr; ++i) {
- size_t iov_len = vec[i].iov_len;
- if (iov_len > len) {
- iov_len = len;
- }
- memcpy(cp, vec[i].iov_base, iov_len);
- len -= iov_len;
- if (len == 0) {
- break;
- }
- cp += iov_len;
- }
-
- return LogBufferLog(&logbuf, element);
-}
-
-static int writeToLocalVersion(struct android_log_logger* logger __unused,
- struct android_log_transport_context* transp
- __unused) {
- return 3;
-}
-
-/* within reader lock, serviceName already validated */
-static struct listnode* writeToLocalNode(
- struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp) {
- struct listnode* node;
- unsigned logMask;
- unsigned int tail;
-
- node = transp->context.node;
- if (node) {
- return node;
- }
-
- if (!logger_list->tail) {
- return transp->context.node = &logbuf.head;
- }
-
- logMask = transp->logMask;
- tail = logger_list->tail;
-
- for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) {
- struct LogBufferElement* element;
- log_id_t logId;
-
- element = node_to_item(node, struct LogBufferElement, node);
- logId = element->logId;
-
- if ((logMask & (1 << logId)) && !--tail) {
- node = node->next;
- break;
- }
- }
- return transp->context.node = node;
-}
-
-static int writeToLocalRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
- int ret;
- struct listnode* node;
- unsigned logMask;
-
- pthread_rwlock_rdlock(&logbuf.listLock);
- if (!logbuf.serviceName) {
- pthread_rwlock_unlock(&logbuf.listLock);
- return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
- }
-
- logMask = transp->logMask;
-
- node = writeToLocalNode(logger_list, transp);
-
- ret = 0;
-
- while (node != list_head(&logbuf.head)) {
- struct LogBufferElement* element;
- log_id_t logId;
-
- node = node->prev;
- element = node_to_item(node, struct LogBufferElement, node);
- logId = element->logId;
-
- if (logMask & (1 << logId)) {
- ret = log_msg->entry_v3.len = element->len;
- log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
- log_msg->entry_v3.pid = getpid();
- log_msg->entry_v3.tid = element->tid;
- log_msg->entry_v3.sec = element->timestamp.tv_sec;
- log_msg->entry_v3.nsec = element->timestamp.tv_nsec;
- log_msg->entry_v3.lid = logId;
- memcpy(log_msg->entry_v3.msg, element->msg, ret);
- ret += log_msg->entry_v3.hdr_size;
- break;
- }
- }
-
- transp->context.node = node;
-
- /* ToDo: if blocking, and no entry, put reader to sleep */
- pthread_rwlock_unlock(&logbuf.listLock);
- return ret;
-}
-
-static int writeToLocalPoll(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp) {
- int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
-
- pthread_rwlock_rdlock(&logbuf.listLock);
-
- if (logbuf.serviceName) {
- unsigned logMask = transp->logMask;
- struct listnode* node = writeToLocalNode(logger_list, transp);
-
- ret = (node != list_head(&logbuf.head));
- if (ret) {
- do {
- ret = !!(logMask &
- (1 << (node_to_item(node->prev, struct LogBufferElement, node))
- ->logId));
- } while (!ret && ((node = node->prev) != list_head(&logbuf.head)));
- }
-
- transp->context.node = node;
- }
-
- pthread_rwlock_unlock(&logbuf.listLock);
-
- return ret;
-}
-
-static void writeToLocalClose(struct android_log_logger_list* logger_list
- __unused,
- struct android_log_transport_context* transp) {
- pthread_rwlock_wrlock(&logbuf.listLock);
- transp->context.node = list_head(&logbuf.head);
- pthread_rwlock_unlock(&logbuf.listLock);
-}
-
-static int writeToLocalClear(struct android_log_logger* logger,
- struct android_log_transport_context* unused
- __unused) {
- log_id_t logId = logger->logId;
- struct listnode *node, *n;
-
- if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
- return -EINVAL;
- }
-
- pthread_rwlock_wrlock(&logbuf.listLock);
- logbuf.number[logId] = 0;
- logbuf.last[logId] = &logbuf.head;
- list_for_each_safe(node, n, &logbuf.head) {
- struct LogBufferElement* element;
- element = node_to_item(node, struct LogBufferElement, node);
-
- if (logId == element->logId) {
- struct android_log_logger_list* logger_list;
-
- logger_list_rdlock();
- logger_list_for_each(logger_list) {
- struct android_log_transport_context* transp;
-
- transport_context_for_each(transp, logger_list) {
- if ((transp->transport == &localLoggerRead) &&
- (transp->context.node == node)) {
- transp->context.node = node->next;
- }
- }
- }
- logger_list_unlock();
- list_remove(node);
- free(element);
- }
- }
-
- pthread_rwlock_unlock(&logbuf.listLock);
-
- return 0;
-}
-
-static ssize_t writeToLocalGetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp
- __unused) {
- ssize_t ret = -EINVAL;
- log_id_t logId = logger->logId;
-
- if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
- pthread_rwlock_rdlock(&logbuf.listLock);
- ret = logbuf.maxSize[logId];
- pthread_rwlock_unlock(&logbuf.listLock);
- }
-
- return ret;
-}
-
-static ssize_t writeToLocalSetSize(
- struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused, size_t size) {
- ssize_t ret = -EINVAL;
-
- if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) {
- log_id_t logId = logger->logId;
- if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) {
- pthread_rwlock_wrlock(&logbuf.listLock);
- ret = logbuf.maxSize[logId] = size;
- pthread_rwlock_unlock(&logbuf.listLock);
- }
- }
-
- return ret;
-}
-
-static ssize_t writeToLocalGetReadbleSize(
- struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused) {
- ssize_t ret = -EINVAL;
- log_id_t logId = logger->logId;
-
- if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
- pthread_rwlock_rdlock(&logbuf.listLock);
- ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF;
- pthread_rwlock_unlock(&logbuf.listLock);
- }
-
- return ret;
-}
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.cpp
similarity index 83%
rename from liblog/log_event_list.c
rename to liblog/log_event_list.cpp
index 14002ce..b1b527c 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.cpp
@@ -29,21 +29,26 @@
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
-typedef struct {
+enum ReadWriteFlag {
+ kAndroidLoggerRead = 1,
+ kAndroidLoggerWrite = 2,
+};
+
+struct android_log_context_internal {
uint32_t tag;
- unsigned pos; /* Read/write position into buffer */
+ unsigned pos; /* Read/write position into buffer */
unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
unsigned list_nest_depth;
unsigned len; /* Length or raw buffer. */
bool overflow;
bool list_stop; /* next call decrement list_nest_depth and issue a stop */
- enum {
- kAndroidLoggerRead = 1,
- kAndroidLoggerWrite = 2,
- } read_write_flag;
+ ReadWriteFlag read_write_flag;
uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
-} android_log_context_internal;
+};
+
+// TODO(tomcherry): real C++ structs.
+typedef struct android_log_context_internal android_log_context_internal;
static void init_context(android_log_context_internal* context, uint32_t tag) {
size_t needed;
@@ -60,18 +65,19 @@
context->pos += needed;
}
-static void init_parser_context(android_log_context_internal* context,
- const char* msg, size_t len) {
+static void init_parser_context(android_log_context_internal* context, const char* msg,
+ size_t len) {
len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
context->len = len;
memcpy(context->storage, msg, len);
context->read_write_flag = kAndroidLoggerRead;
}
-LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
+android_log_context create_android_logger(uint32_t tag) {
android_log_context_internal* context;
- context = calloc(1, sizeof(android_log_context_internal));
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
if (!context) {
return NULL;
}
@@ -80,12 +86,12 @@
return (android_log_context)context;
}
-LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(const char* msg,
- size_t len) {
+android_log_context create_android_log_parser(const char* msg, size_t len) {
android_log_context_internal* context;
size_t i;
- context = calloc(1, sizeof(android_log_context_internal));
+ context =
+ static_cast<android_log_context_internal*>(calloc(1, sizeof(android_log_context_internal)));
if (!context) {
return NULL;
}
@@ -94,7 +100,7 @@
return (android_log_context)context;
}
-LIBLOG_ABI_PUBLIC int android_log_destroy(android_log_context* ctx) {
+int android_log_destroy(android_log_context* ctx) {
android_log_context_internal* context;
context = (android_log_context_internal*)*ctx;
@@ -107,7 +113,7 @@
return 0;
}
-LIBLOG_ABI_PUBLIC int android_log_reset(android_log_context ctx) {
+int android_log_reset(android_log_context ctx) {
android_log_context_internal* context;
uint32_t tag;
@@ -123,8 +129,7 @@
return 0;
}
-LIBLOG_ABI_PUBLIC int android_log_parser_reset(android_log_context ctx,
- const char* msg, size_t len) {
+int android_log_parser_reset(android_log_context ctx, const char* msg, size_t len) {
android_log_context_internal* context;
context = (android_log_context_internal*)ctx;
@@ -138,8 +143,7 @@
return 0;
}
-
-LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
+int android_log_write_list_begin(android_log_context ctx) {
size_t needed;
android_log_context_internal* context;
@@ -180,8 +184,7 @@
buf[3] = (val >> 24) & 0xFF;
}
-LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
- int32_t value) {
+int android_log_write_int32(android_log_context ctx, int32_t value) {
size_t needed;
android_log_context_internal* context;
@@ -215,8 +218,7 @@
buf[7] = (val >> 56) & 0xFF;
}
-LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
- int64_t value) {
+int android_log_write_int64(android_log_context ctx, int64_t value) {
size_t needed;
android_log_context_internal* context;
@@ -239,9 +241,7 @@
return 0;
}
-LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx,
- const char* value,
- size_t maxlen) {
+int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
size_t needed;
ssize_t len;
android_log_context_internal* context;
@@ -276,13 +276,11 @@
return len;
}
-LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx,
- const char* value) {
+int android_log_write_string8(android_log_context ctx, const char* value) {
return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
}
-LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
- float value) {
+int android_log_write_float32(android_log_context ctx, float value) {
size_t needed;
uint32_t ivalue;
android_log_context_internal* context;
@@ -307,7 +305,7 @@
return 0;
}
-LIBLOG_ABI_PUBLIC int android_log_write_list_end(android_log_context ctx) {
+int android_log_write_list_end(android_log_context ctx) {
android_log_context_internal* context;
context = (android_log_context_internal*)ctx;
@@ -337,8 +335,7 @@
/*
* Logs the list of elements to the event log.
*/
-LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
- log_id_t id) {
+int android_log_write_list(android_log_context ctx, log_id_t id) {
android_log_context_internal* context;
const char* msg;
ssize_t len;
@@ -368,13 +365,11 @@
}
return (id == LOG_ID_EVENTS)
? __android_log_bwrite(context->tag, msg, len)
- : ((id == LOG_ID_STATS)
- ? __android_log_stats_bwrite(context->tag, msg, len)
- : __android_log_security_bwrite(context->tag, msg, len));
+ : ((id == LOG_ID_STATS) ? __android_log_stats_bwrite(context->tag, msg, len)
+ : __android_log_security_bwrite(context->tag, msg, len));
}
-LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
- const char** buffer) {
+int android_log_write_list_buffer(android_log_context ctx, const char** buffer) {
android_log_context_internal* context;
const char* msg;
ssize_t len;
@@ -428,8 +423,7 @@
* this and continues to call this function, the behavior is undefined
* (although it won't crash).
*/
-static android_log_list_element android_log_read_next_internal(
- android_log_context ctx, int peek) {
+static android_log_list_element android_log_read_next_internal(android_log_context ctx, int peek) {
android_log_list_element elem;
unsigned pos;
android_log_context_internal* context;
@@ -444,9 +438,9 @@
(context->count[context->list_nest_depth] >=
(MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
elem.type = EVENT_TYPE_UNKNOWN;
- if (context && (context->list_stop ||
- ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
- !context->count[context->list_nest_depth]))) {
+ if (context &&
+ (context->list_stop || ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+ !context->count[context->list_nest_depth]))) {
elem.type = EVENT_TYPE_LIST_STOP;
}
elem.complete = true;
@@ -460,9 +454,8 @@
pos = context->pos;
if (context->list_stop) {
elem.type = EVENT_TYPE_LIST_STOP;
- elem.complete = !context->count[0] &&
- (!context->list_nest_depth ||
- ((context->list_nest_depth == 1) && !context->count[1]));
+ elem.complete = !context->count[0] && (!context->list_nest_depth ||
+ ((context->list_nest_depth == 1) && !context->count[1]));
if (!peek) {
/* Suck in superfluous stop */
if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
@@ -485,7 +478,7 @@
return elem;
}
- elem.type = context->storage[pos++];
+ elem.type = static_cast<AndroidEventLogType>(context->storage[pos++]);
switch ((int)elem.type) {
case EVENT_TYPE_FLOAT:
/* Rely on union to translate elem.data.int32 into elem.data.float32 */
@@ -598,12 +591,10 @@
}
}
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_read_next(android_log_context ctx) {
+android_log_list_element android_log_read_next(android_log_context ctx) {
return android_log_read_next_internal(ctx, 0);
}
-LIBLOG_ABI_PUBLIC android_log_list_element
-android_log_peek_next(android_log_context ctx) {
+android_log_list_element android_log_peek_next(android_log_context ctx) {
return android_log_read_next_internal(ctx, 1);
}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.cpp
similarity index 85%
rename from liblog/log_event_write.c
rename to liblog/log_event_write.cpp
index 45a6f37..d04ba90 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.cpp
@@ -24,9 +24,8 @@
#define MAX_SUBTAG_LEN 32
-LIBLOG_ABI_PUBLIC int __android_log_error_write(int tag, const char* subTag,
- int32_t uid, const char* data,
- uint32_t dataLen) {
+int __android_log_error_write(int tag, const char* subTag, int32_t uid, const char* data,
+ uint32_t dataLen) {
int ret = -EINVAL;
if (subTag && (data || !dataLen)) {
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index 88805c7..b7279d1 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -14,41 +14,11 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_PORTABILITY_H__
-#define _LIBLOG_PORTABILITY_H__
+#pragma once
#include <sys/cdefs.h>
#include <unistd.h>
-/* Helpful private sys/cdefs.h like definitions */
-
-/* Declare this library function hidden and internal */
-#if defined(_WIN32)
-#define LIBLOG_HIDDEN
-#else
-#define LIBLOG_HIDDEN __attribute__((visibility("hidden")))
-#endif
-
-/* Declare this library function visible and external */
-#if defined(_WIN32)
-#define LIBLOG_ABI_PUBLIC
-#else
-#define LIBLOG_ABI_PUBLIC __attribute__((visibility("default")))
-#endif
-
-/* Declare this library function visible but private */
-#define LIBLOG_ABI_PRIVATE LIBLOG_ABI_PUBLIC
-
-/*
- * Declare this library function as reimplementation.
- * Prevent circular dependencies, but allow _real_ library to hijack
- */
-#if defined(_WIN32)
-#define LIBLOG_WEAK static /* Accept that it is totally private */
-#else
-#define LIBLOG_WEAK __attribute__((weak, visibility("default")))
-#endif
-
/* possible missing definitions in sys/cdefs.h */
/* DECLS */
@@ -62,11 +32,6 @@
#endif
#endif
-/* Unused argument. For C code only, remove symbol name for C++ */
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
/* possible missing definitions in unistd.h */
#ifndef TEMP_FAILURE_RETRY
@@ -80,5 +45,3 @@
_rc; \
})
#endif
-
-#endif /* _LIBLOG_PORTABILITY_H__ */
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
deleted file mode 100644
index 33770dd..0000000
--- a/liblog/log_ratelimit.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <errno.h>
-#include <pthread.h>
-#include <time.h>
-
-#include <log/log.h>
-
-#include "log_portability.h"
-
-// Global default if 'last' argument in __android_log_ratelimit is NULL
-static time_t g_last_clock;
-// Global above can not deal well with callers playing games with the
-// seconds argument, so we will also hold on to the maximum value
-// ever provided and use that to gain consistency. If the caller
-// provides their own 'last' argument, then they can play such games
-// of varying the 'seconds' argument to their pleasure.
-static time_t g_last_seconds;
-static const time_t last_seconds_default = 10;
-static const time_t last_seconds_max = 24 * 60 * 60; // maximum of a day
-static const time_t last_seconds_min = 2; // granularity
-// Lock to protect last_clock and last_seconds, but also 'last'
-// argument (not NULL) as supplied to __android_log_ratelimit.
-static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
-
-// if last is NULL, caller _must_ provide a consistent value for
-// seconds, otherwise we will take the maximum ever issued and hold
-// on to that. Preserves value of non-zero errno. Return -1 if we
-// can not acquire a lock, 0 if we are not to log a message, and 1
-// if we are ok to log a message. Caller should check > 0 for true.
-LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
- int save_errno = errno;
-
- // Two reasons for trylock failure:
- // 1. In a signal handler. Must prevent deadlock
- // 2. Too many threads calling __android_log_ratelimit.
- // Bonus to not print if they race here because that
- // dovetails the goal of ratelimiting. One may print
- // and the others will wait their turn ...
- if (pthread_mutex_trylock(&lock_ratelimit)) {
- if (save_errno) errno = save_errno;
- return -1;
- }
-
- if (seconds == 0) {
- seconds = last_seconds_default;
- } else if (seconds < last_seconds_min) {
- seconds = last_seconds_min;
- } else if (seconds > last_seconds_max) {
- seconds = last_seconds_max;
- }
-
- if (!last) {
- if (g_last_seconds > seconds) {
- seconds = g_last_seconds;
- } else if (g_last_seconds < seconds) {
- g_last_seconds = seconds;
- }
- last = &g_last_clock;
- }
-
- time_t now = time(NULL);
- if ((now == (time_t)-1) || ((*last + seconds) > now)) {
- pthread_mutex_unlock(&lock_ratelimit);
- if (save_errno) errno = save_errno;
- return 0;
- }
- *last = now;
- pthread_mutex_unlock(&lock_ratelimit);
- if (save_errno) errno = save_errno;
- return 1;
-}
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index ae376be..3ae250f 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -23,12 +23,12 @@
#include "log_portability.h"
-LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
+const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+const timespec log_time::EPOCH = {0, 0};
// Add %#q for fractional seconds to standard strptime function
-LIBLOG_ABI_PRIVATE char* log_time::strptime(const char* s, const char* format) {
+char* log_time::strptime(const char* s, const char* format) {
time_t now;
#ifdef __linux__
*this = log_time(CLOCK_REALTIME);
@@ -134,10 +134,10 @@
return ret;
}
-LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const timespec& T) {
+log_time log_time::operator-=(const timespec& T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
- return *this = EPOCH;
+ return *this = log_time(EPOCH);
}
if (this->tv_nsec < (unsigned long int)T.tv_nsec) {
@@ -151,7 +151,7 @@
return *this;
}
-LIBLOG_ABI_PRIVATE log_time log_time::operator+=(const timespec& T) {
+log_time log_time::operator+=(const timespec& T) {
this->tv_nsec += (unsigned long int)T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
@@ -162,10 +162,10 @@
return *this;
}
-LIBLOG_ABI_PRIVATE log_time log_time::operator-=(const log_time& T) {
+log_time log_time::operator-=(const log_time& T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
- return *this = EPOCH;
+ return *this = log_time(EPOCH);
}
if (this->tv_nsec < T.tv_nsec) {
@@ -179,7 +179,7 @@
return *this;
}
-LIBLOG_ABI_PRIVATE log_time log_time::operator+=(const log_time& T) {
+log_time log_time::operator+=(const log_time& T) {
this->tv_nsec += T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.cpp
similarity index 67%
rename from liblog/logd_reader.c
rename to liblog/logd_reader.cpp
index 603ba24..b7ba782 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.cpp
@@ -47,8 +47,7 @@
static int logdVersion(struct android_log_logger* logger,
struct android_log_transport_context* transp);
static int logdRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg);
+ struct android_log_transport_context* transp, struct log_msg* log_msg);
static int logdPoll(struct android_log_logger_list* logger_list,
struct android_log_transport_context* transp);
static void logdClose(struct android_log_logger_list* logger_list,
@@ -56,37 +55,33 @@
static int logdClear(struct android_log_logger* logger,
struct android_log_transport_context* transp);
static ssize_t logdSetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp,
- size_t size);
+ struct android_log_transport_context* transp, size_t size);
static ssize_t logdGetSize(struct android_log_logger* logger,
struct android_log_transport_context* transp);
static ssize_t logdGetReadableSize(struct android_log_logger* logger,
struct android_log_transport_context* transp);
static ssize_t logdGetPrune(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp,
- char* buf, size_t len);
+ struct android_log_transport_context* transp, char* buf, size_t len);
static ssize_t logdSetPrune(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp,
- char* buf, size_t len);
+ struct android_log_transport_context* transp, char* buf, size_t len);
static ssize_t logdGetStats(struct android_log_logger_list* logger,
- struct android_log_transport_context* transp,
- char* buf, size_t len);
+ struct android_log_transport_context* transp, char* buf, size_t len);
-LIBLOG_HIDDEN struct android_log_transport_read logdLoggerRead = {
- .node = { &logdLoggerRead.node, &logdLoggerRead.node },
- .name = "logd",
- .available = logdAvailable,
- .version = logdVersion,
- .read = logdRead,
- .poll = logdPoll,
- .close = logdClose,
- .clear = logdClear,
- .getSize = logdGetSize,
- .setSize = logdSetSize,
- .getReadableSize = logdGetReadableSize,
- .getPrune = logdGetPrune,
- .setPrune = logdSetPrune,
- .getStats = logdGetStats,
+struct android_log_transport_read logdLoggerRead = {
+ .node = {&logdLoggerRead.node, &logdLoggerRead.node},
+ .name = "logd",
+ .available = logdAvailable,
+ .version = logdVersion,
+ .read = logdRead,
+ .poll = logdPoll,
+ .close = logdClose,
+ .clear = logdClear,
+ .getSize = logdGetSize,
+ .setSize = logdSetSize,
+ .getReadableSize = logdGetReadableSize,
+ .getPrune = logdGetPrune,
+ .setPrune = logdSetPrune,
+ .getStats = logdGetStats,
};
static int logdAvailable(log_id_t logId) {
@@ -105,164 +100,38 @@
return -EBADF;
}
-/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
+// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
+// O_CLOEXEC is always set.
+static int socket_local_client(const std::string& name, int type) {
+ sockaddr_un addr = {.sun_family = AF_LOCAL};
-#if defined(_WIN32)
-
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId,
- int type) {
- errno = ENOSYS;
- return -ENOSYS;
-}
-
-#else /* !_WIN32 */
-
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
-/* Private copy of ../libcutils/socket_local.h prevent library loops */
-#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
-#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
-/* End of ../libcutils/socket_local.h */
-
-#define LISTEN_BACKLOG 4
-
-/* Documented in header file. */
-LIBLOG_WEAK int socket_make_sockaddr_un(const char* name, int namespaceId,
- struct sockaddr_un* p_addr,
- socklen_t* alen) {
- memset(p_addr, 0, sizeof(*p_addr));
- size_t namelen;
-
- switch (namespaceId) {
- case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
-#if defined(__linux__)
- namelen = strlen(name);
-
- /* Test with length +1 for the *initial* '\0'. */
- if ((namelen + 1) > sizeof(p_addr->sun_path)) {
- goto error;
- }
-
- /*
- * Note: The path in this case is *not* supposed to be
- * '\0'-terminated. ("man 7 unix" for the gory details.)
- */
-
- p_addr->sun_path[0] = 0;
- memcpy(p_addr->sun_path + 1, name, namelen);
-#else
- /* this OS doesn't have the Linux abstract namespace */
-
- namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen >
- sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
-#endif
- break;
-
- case ANDROID_SOCKET_NAMESPACE_RESERVED:
- namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
- /* unix_path_max appears to be missing on linux */
- if (namelen >
- sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
- strcat(p_addr->sun_path, name);
- break;
-
- case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
- namelen = strlen(name);
- /* unix_path_max appears to be missing on linux */
- if (namelen >
- sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1) {
- goto error;
- }
-
- strcpy(p_addr->sun_path, name);
- break;
-
- default:
- /* invalid namespace id */
- return -1;
+ std::string path = "/dev/socket/" + name;
+ if (path.size() + 1 > sizeof(addr.sun_path)) {
+ return -1;
}
+ strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
- p_addr->sun_family = AF_LOCAL;
- *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
- return 0;
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name" on fd
- * returns same fd or -1 on error.
- * fd is not closed on error. that's your job.
- *
- * Used by AndroidSocketImpl
- */
-LIBLOG_WEAK int socket_local_client_connect(int fd, const char* name,
- int namespaceId, int type __unused) {
- struct sockaddr_un addr;
- socklen_t alen;
- int err;
-
- err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
-
- if (err < 0) {
- goto error;
- }
-
- if (connect(fd, (struct sockaddr*)&addr, alen) < 0) {
- goto error;
- }
-
- return fd;
-
-error:
- return -1;
-}
-
-/**
- * connect to peer named "name"
- * returns fd or -1 on error
- */
-LIBLOG_WEAK int socket_local_client(const char* name, int namespaceId,
- int type) {
- int s;
-
- s = socket(AF_LOCAL, type, 0);
- if (s < 0) return -1;
-
- if (0 > socket_local_client_connect(s, name, namespaceId, type)) {
- close(s);
+ int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
+ if (fd == 0) {
return -1;
}
- return s;
+ if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
}
-#endif /* !_WIN32 */
-/* End of ../libcutils/socket_local_client.c */
-
/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg,
- char* buf, size_t buf_size) {
+static ssize_t send_log_msg(struct android_log_logger* logger, const char* msg, char* buf,
+ size_t buf_size) {
ssize_t ret;
size_t len;
char* cp;
int errno_save = 0;
- int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
+ int sock = socket_local_client("logd", SOCK_STREAM);
if (sock < 0) {
return sock;
}
@@ -321,7 +190,7 @@
return ret;
}
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf, size_t buf_size) {
+ssize_t __send_log_msg(char* buf, size_t buf_size) {
return send_log_msg(NULL, NULL, buf, buf_size);
}
@@ -342,8 +211,7 @@
struct android_log_transport_context* transp __unused) {
char buf[512];
- return check_log_success(buf,
- send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+ return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
}
/* returns the total size of the log's ring buffer */
@@ -364,8 +232,7 @@
}
static ssize_t logdSetSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp __unused,
- size_t size) {
+ struct android_log_transport_context* transp __unused, size_t size) {
char buf[512];
snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
@@ -378,8 +245,7 @@
* log consumed)
*/
static ssize_t logdGetReadableSize(struct android_log_logger* logger,
- struct android_log_transport_context* transp
- __unused) {
+ struct android_log_transport_context* transp __unused) {
char buf[512];
ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
@@ -407,8 +273,8 @@
* returns statistics
*/
static ssize_t logdGetStats(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp __unused,
- char* buf, size_t len) {
+ struct android_log_transport_context* transp __unused, char* buf,
+ size_t len) {
struct android_log_logger* logger;
char* cp = buf;
size_t remaining = len;
@@ -434,14 +300,14 @@
}
static ssize_t logdGetPrune(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp __unused,
- char* buf, size_t len) {
+ struct android_log_transport_context* transp __unused, char* buf,
+ size_t len) {
return send_log_msg(NULL, "getPruneList", buf, len);
}
static ssize_t logdSetPrune(struct android_log_logger_list* logger_list __unused,
- struct android_log_transport_context* transp __unused,
- char* buf, size_t len) {
+ struct android_log_transport_context* transp __unused, char* buf,
+ size_t len) {
const char cmd[] = "setPruneList ";
const size_t cmdlen = sizeof(cmd) - 1;
@@ -455,8 +321,7 @@
return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
}
-static void caught_signal(int signum __unused) {
-}
+static void caught_signal(int signum __unused) {}
static int logdOpen(struct android_log_logger_list* logger_list,
struct android_log_transport_context* transp) {
@@ -476,12 +341,10 @@
return sock;
}
- sock = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_SEQPACKET);
+ sock = socket_local_client("logdr", SOCK_SEQPACKET);
if (sock == 0) {
/* Guarantee not file descriptor zero */
- int newsock = socket_local_client(
- "logdr", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);
+ int newsock = socket_local_client("logdr", SOCK_SEQPACKET);
close(sock);
sock = newsock;
}
@@ -492,8 +355,7 @@
return sock;
}
- strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose"
- : "stream");
+ strcpy(buffer, (logger_list->mode & ANDROID_LOG_NONBLOCK) ? "dumpAndClose" : "stream");
cp = buffer + strlen(buffer);
strcpy(cp, " lids");
@@ -518,14 +380,13 @@
if (logger_list->start.tv_sec || logger_list->start.tv_nsec) {
if (logger_list->mode & ANDROID_LOG_WRAP) {
// ToDo: alternate API to allow timeout to be adjusted.
- ret = snprintf(cp, remaining, " timeout=%u",
- ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
+ ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
ret = min(ret, remaining);
remaining -= ret;
cp += ret;
}
- ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32,
- logger_list->start.tv_sec, logger_list->start.tv_nsec);
+ ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
+ logger_list->start.tv_nsec);
ret = min(ret, remaining);
remaining -= ret;
cp += ret;
@@ -576,8 +437,7 @@
/* Read from the selected logs */
static int logdRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
+ struct android_log_transport_context* transp, struct log_msg* log_msg) {
int ret, e;
struct sigaction ignore;
struct sigaction old_sigaction;
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 8ebb1ae..7c53cbb 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_LOGD_READER_H__
-#define _LIBLOG_LOGD_READER_H__
+#pragma once
#include <unistd.h>
@@ -23,8 +22,6 @@
__BEGIN_DECLS
-LIBLOG_HIDDEN ssize_t __send_log_msg(char* buf, size_t buf_size);
+ssize_t __send_log_msg(char* buf, size_t buf_size);
__END_DECLS
-
-#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logd_writer.c b/liblog/logd_writer.cpp
similarity index 85%
rename from liblog/logd_writer.c
rename to liblog/logd_writer.cpp
index e0e3eca..c3f72f4 100644
--- a/liblog/logd_writer.c
+++ b/liblog/logd_writer.cpp
@@ -38,6 +38,7 @@
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
+#include "uio.h"
/* branchless on many architectures. */
#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
@@ -45,17 +46,16 @@
static int logdAvailable(log_id_t LogId);
static int logdOpen();
static void logdClose();
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr);
+static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
- .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
- .context.sock = -EBADF,
- .name = "logd",
- .available = logdAvailable,
- .open = logdOpen,
- .close = logdClose,
- .write = logdWrite,
+struct android_log_transport_write logdLoggerWrite = {
+ .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
+ .context.sock = -EBADF,
+ .name = "logd",
+ .available = logdAvailable,
+ .open = logdOpen,
+ .close = logdClose,
+ .write = logdWrite,
};
/* log_init_lock assumed */
@@ -64,8 +64,7 @@
i = atomic_load(&logdLoggerWrite.context.sock);
if (i < 0) {
- int sock = TEMP_FAILURE_RETRY(
- socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
if (sock < 0) {
ret = -errno;
} else {
@@ -74,15 +73,15 @@
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
- if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un,
- sizeof(struct sockaddr_un))) < 0) {
+ if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) <
+ 0) {
ret = -errno;
switch (ret) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
- /* FALLTHRU */
+ [[fallthrough]];
default:
break;
}
@@ -124,16 +123,15 @@
return 1;
}
-static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr) {
+static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
ssize_t ret;
int sock;
static const unsigned headerLength = 1;
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int_fast32_t dropped;
- static atomic_int_fast32_t droppedSecurity;
+ static atomic_int dropped;
+ static atomic_int droppedSecurity;
sock = atomic_load(&logdLoggerWrite.context.sock);
if (sock < 0) switch (sock) {
@@ -181,8 +179,7 @@
newVec[0].iov_len = sizeof(header);
if (sock >= 0) {
- int32_t snapshot =
- atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
+ int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
if (snapshot) {
android_log_event_int_t buffer;
@@ -196,14 +193,12 @@
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&droppedSecurity, snapshot,
- memory_order_relaxed);
+ atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
}
}
snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot &&
- __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog",
- strlen("liblog"), ANDROID_LOG_VERBOSE)) {
+ if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
+ ANDROID_LOG_VERBOSE)) {
android_log_event_int_t buffer;
header.id = LOG_ID_EVENTS;
@@ -267,12 +262,11 @@
return ret;
}
- ret = TEMP_FAILURE_RETRY(
- writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
+ ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
if (ret < 0) {
ret = -errno;
}
- /* FALLTHRU */
+ [[fallthrough]];
default:
break;
}
diff --git a/liblog/logger.h b/liblog/logger.h
index 246b33c..1f632c0 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -14,22 +14,21 @@
* limitations under the License.
*/
-#ifndef _LIBLOG_LOGGER_H__
-#define _LIBLOG_LOGGER_H__
+#pragma once
#include <stdatomic.h>
#include <stdbool.h>
#include <cutils/list.h>
#include <log/log.h>
-#include <log/uio.h>
#include "log_portability.h"
+#include "uio.h"
__BEGIN_DECLS
/* Union, sock or fd of zero is not allowed unless static initialized */
-union android_log_context {
+union android_log_context_union {
void* priv;
atomic_int sock;
atomic_int fd;
@@ -41,7 +40,7 @@
struct listnode node;
const char* name; /* human name to describe the transport */
unsigned logMask; /* mask cache of available() success */
- union android_log_context context; /* Initialized by static allocation */
+ union android_log_context_union context; /* Initialized by static allocation */
int (*available)(log_id_t logId); /* Does not cause resources to be taken */
int (*open)(); /* can be called multiple times, reusing current resources */
@@ -98,7 +97,6 @@
};
struct android_log_logger_list {
- struct listnode node;
struct listnode logger;
struct listnode transport;
int mode;
@@ -116,7 +114,7 @@
struct android_log_transport_context {
struct listnode node;
- union android_log_context context; /* zero init per-transport context */
+ union android_log_context_union context; /* zero init per-transport context */
struct android_log_logger_list* parent;
struct android_log_transport_read* transport;
@@ -144,37 +142,6 @@
(logp) = \
node_to_item((logp)->node.next, struct android_log_logger, node))
-/*
- * Global list of log readers.
- *
- * Usage case: search out transport contexts for all readers
- */
-
-LIBLOG_HIDDEN struct listnode __android_log_readers;
-
-#if defined(_WIN32)
-#define logger_list_rdlock()
-#define logger_list_wrlock()
-#define logger_list_unlock()
-#else
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock;
-
-#define logger_list_rdlock() pthread_rwlock_rdlock(&__android_log_readers_lock)
-#define logger_list_wrlock() pthread_rwlock_wrlock(&__android_log_readers_lock)
-#define logger_list_unlock() pthread_rwlock_unlock(&__android_log_readers_lock)
-#endif
-
-/* Must be called with logger_list_rdlock() or logger_list_wrlock() held */
-#define logger_list_for_each(logger_list) \
- for ((logger_list) = node_to_item(&__android_log_readers, \
- struct android_log_logger_list, node); \
- (logger_list) != node_to_item(&__android_log_readers, \
- struct android_log_logger_list, node) && \
- (logger_list) != node_to_item((logger_list)->node.next, \
- struct android_log_logger_list, node); \
- (logger_list) = node_to_item((logger_list)->node.next, \
- struct android_log_logger_list, node))
-
/* OS specific dribs and drabs */
#if defined(_WIN32)
@@ -189,12 +156,10 @@
}
#endif
-LIBLOG_HIDDEN void __android_log_lock();
-LIBLOG_HIDDEN int __android_log_trylock();
-LIBLOG_HIDDEN void __android_log_unlock();
+void __android_log_lock();
+int __android_log_trylock();
+void __android_log_unlock();
-LIBLOG_HIDDEN int __android_log_transport;
+extern int __android_log_transport;
__END_DECLS
-
-#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_lock.c b/liblog/logger_lock.cpp
similarity index 90%
rename from liblog/logger_lock.c
rename to liblog/logger_lock.cpp
index d4e3a75..4636b00 100644
--- a/liblog/logger_lock.c
+++ b/liblog/logger_lock.cpp
@@ -28,7 +28,7 @@
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
#endif
-LIBLOG_HIDDEN void __android_log_lock() {
+void __android_log_lock() {
#if !defined(_WIN32)
/*
* If we trigger a signal handler in the middle of locked activity and the
@@ -38,7 +38,7 @@
#endif
}
-LIBLOG_HIDDEN int __android_log_trylock() {
+int __android_log_trylock() {
#if !defined(_WIN32)
return pthread_mutex_trylock(&log_init_lock);
#else
@@ -46,7 +46,7 @@
#endif
}
-LIBLOG_HIDDEN void __android_log_unlock() {
+void __android_log_unlock() {
#if !defined(_WIN32)
pthread_mutex_unlock(&log_init_lock);
#endif
diff --git a/liblog/logger_name.c b/liblog/logger_name.cpp
similarity index 74%
rename from liblog/logger_name.c
rename to liblog/logger_name.cpp
index 479bbfe..ece0550 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.cpp
@@ -15,6 +15,7 @@
*/
#include <string.h>
+#include <type_traits>
#include <log/log.h>
@@ -22,7 +23,7 @@
/* In the future, we would like to make this list extensible */
static const char* LOG_NAME[LOG_ID_MAX] = {
- /* clang-format off */
+ /* clang-format off */
[LOG_ID_MAIN] = "main",
[LOG_ID_RADIO] = "radio",
[LOG_ID_EVENTS] = "events",
@@ -31,23 +32,27 @@
[LOG_ID_STATS] = "stats",
[LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
- /* clang-format on */
+ /* clang-format on */
};
-LIBLOG_ABI_PUBLIC const char* android_log_id_to_name(log_id_t log_id) {
+const char* android_log_id_to_name(log_id_t log_id) {
if (log_id >= LOG_ID_MAX) {
log_id = LOG_ID_MAIN;
}
return LOG_NAME[log_id];
}
-LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char* logName) {
+static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value,
+ "log_id_t must be an unsigned int");
+
+log_id_t android_name_to_log_id(const char* logName) {
const char* b;
- int ret;
+ unsigned int ret;
if (!logName) {
- return -1; /* NB: log_id_t is unsigned */
+ return static_cast<log_id_t>(LOG_ID_MAX);
}
+
b = strrchr(logName, '/');
if (!b) {
b = logName;
@@ -58,8 +63,9 @@
for (ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
const char* l = LOG_NAME[ret];
if (l && !strcmp(b, l)) {
- return ret;
+ return static_cast<log_id_t>(ret);
}
}
- return -1; /* should never happen */
+
+ return static_cast<log_id_t>(LOG_ID_MAX);
}
diff --git a/liblog/logger_read.c b/liblog/logger_read.cpp
similarity index 73%
rename from liblog/logger_read.c
rename to liblog/logger_read.cpp
index 0fd6efa..4cf0846 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+#include "log/log_read.h"
+
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
@@ -34,8 +36,7 @@
/* android_logger_alloc unimplemented, no use case */
/* android_logger_free not exported */
static void android_logger_free(struct logger* logger) {
- struct android_log_logger* logger_internal =
- (struct android_log_logger*)logger;
+ struct android_log_logger* logger_internal = (struct android_log_logger*)logger;
if (!logger_internal) {
return;
@@ -49,7 +50,7 @@
/* android_logger_alloc unimplemented, no use case */
/* method for getting the associated sublog id */
-LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger* logger) {
+log_id_t android_logger_get_id(struct logger* logger) {
return ((struct android_log_logger*)logger)->logId;
}
@@ -71,15 +72,13 @@
__android_log_lock();
/* mini __write_to_log_initialize() to populate transports */
- if (list_empty(&__android_log_transport_read) &&
- list_empty(&__android_log_persist_read)) {
+ if (list_empty(&__android_log_transport_read) && list_empty(&__android_log_persist_read)) {
__android_log_config_read();
}
__android_log_unlock();
- node = (logger_list->mode & ANDROID_LOG_PSTORE)
- ? &__android_log_persist_read
- : &__android_log_transport_read;
+ node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
+ : &__android_log_transport_read;
read_transport_for_each(transport, node) {
struct android_log_transport_context* transp;
@@ -92,15 +91,14 @@
if ((logId == LOG_ID_SECURITY) && (__android_log_uid() != AID_SYSTEM)) {
continue;
}
- if (transport->read &&
- (!transport->available || (transport->available(logId) >= 0))) {
+ if (transport->read && (!transport->available || (transport->available(logId) >= 0))) {
logMask |= 1 << logId;
}
}
if (!logMask) {
continue;
}
- transp = calloc(1, sizeof(*transp));
+ transp = static_cast<android_log_transport_context*>(calloc(1, sizeof(*transp)));
if (!transp) {
return -ENOMEM;
}
@@ -116,44 +114,41 @@
return 0;
}
-#define LOGGER_FUNCTION(logger, def, func, args...) \
- ssize_t ret = -EINVAL; \
- struct android_log_transport_context* transp; \
- struct android_log_logger* logger_internal = \
- (struct android_log_logger*)(logger); \
- \
- if (!logger_internal) { \
- return ret; \
- } \
- ret = init_transport_context(logger_internal->parent); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_internal->parent) { \
- if ((transp->logMask & (1 << logger_internal->logId)) && \
- transp->transport && transp->transport->func) { \
- ssize_t retval = \
- (transp->transport->func)(logger_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#define LOGGER_FUNCTION(logger, def, func, args...) \
+ ssize_t ret = -EINVAL; \
+ struct android_log_transport_context* transp; \
+ struct android_log_logger* logger_internal = (struct android_log_logger*)(logger); \
+ \
+ if (!logger_internal) { \
+ return ret; \
+ } \
+ ret = init_transport_context(logger_internal->parent); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ transport_context_for_each(transp, logger_internal->parent) { \
+ if ((transp->logMask & (1 << logger_internal->logId)) && transp->transport && \
+ transp->transport->func) { \
+ ssize_t retval = (transp->transport->func)(logger_internal, transp, ##args); \
+ if ((ret >= 0) || (ret == (def))) { \
+ ret = retval; \
+ } \
+ } \
+ } \
return ret
-LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger* logger) {
+int android_logger_clear(struct logger* logger) {
LOGGER_FUNCTION(logger, -ENODEV, clear);
}
/* returns the total size of the log's ring buffer */
-LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger* logger) {
+long android_logger_get_log_size(struct logger* logger) {
LOGGER_FUNCTION(logger, -ENODEV, getSize);
}
-LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger* logger,
- unsigned long size) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
}
@@ -161,70 +156,57 @@
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
- struct logger* logger) {
+long android_logger_get_log_readable_size(struct logger* logger) {
LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
}
/*
* returns the logger version
*/
-LIBLOG_ABI_PUBLIC int android_logger_get_log_version(struct logger* logger) {
+int android_logger_get_log_version(struct logger* logger) {
LOGGER_FUNCTION(logger, 4, version);
}
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
- struct android_log_transport_context* transp; \
- struct android_log_logger_list* logger_list_internal = \
- (struct android_log_logger_list*)(logger_list); \
- \
- ssize_t ret = init_transport_context(logger_list_internal); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- transport_context_for_each(transp, logger_list_internal) { \
- if (transp->transport && (transp->transport->func)) { \
- ssize_t retval = \
- (transp->transport->func)(logger_list_internal, transp, ##args); \
- if ((ret >= 0) || (ret == (def))) { \
- ret = retval; \
- } \
- } \
- } \
+#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
+ struct android_log_transport_context* transp; \
+ struct android_log_logger_list* logger_list_internal = \
+ (struct android_log_logger_list*)(logger_list); \
+ \
+ ssize_t ret = init_transport_context(logger_list_internal); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ \
+ ret = (def); \
+ transport_context_for_each(transp, logger_list_internal) { \
+ if (transp->transport && (transp->transport->func)) { \
+ ssize_t retval = (transp->transport->func)(logger_list_internal, transp, ##args); \
+ if ((ret >= 0) || (ret == (def))) { \
+ ret = retval; \
+ } \
+ } \
+ } \
return ret
/*
* returns statistics
*/
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
- struct logger_list* logger_list, char* buf, size_t len) {
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
}
-LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
- struct logger_list* logger_list, char* buf, size_t len) {
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
}
-LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
- struct logger_list* logger_list, char* buf, size_t len) {
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
}
-LIBLOG_HIDDEN struct listnode __android_log_readers = { &__android_log_readers,
- &__android_log_readers };
-#if !defined(_WIN32)
-LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock =
- PTHREAD_RWLOCK_INITIALIZER;
-#endif
-
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc(
- int mode, unsigned int tail, pid_t pid) {
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
struct android_log_logger_list* logger_list;
- logger_list = calloc(1, sizeof(*logger_list));
+ logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
if (!logger_list) {
return NULL;
}
@@ -235,18 +217,13 @@
logger_list->tail = tail;
logger_list->pid = pid;
- logger_list_wrlock();
- list_add_tail(&__android_log_readers, &logger_list->node);
- logger_list_unlock();
-
return (struct logger_list*)logger_list;
}
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_alloc_time(
- int mode, log_time start, pid_t pid) {
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
struct android_log_logger_list* logger_list;
- logger_list = calloc(1, sizeof(*logger_list));
+ logger_list = static_cast<android_log_logger_list*>(calloc(1, sizeof(*logger_list)));
if (!logger_list) {
return NULL;
}
@@ -257,10 +234,6 @@
logger_list->start = start;
logger_list->pid = pid;
- logger_list_wrlock();
- list_add_tail(&__android_log_readers, &logger_list->node);
- logger_list_unlock();
-
return (struct logger_list*)logger_list;
}
@@ -268,8 +241,7 @@
/* android_logger_list_unregister unimplemented, no use case */
/* Open the named log and add it to the logger list */
-LIBLOG_ABI_PUBLIC struct logger* android_logger_open(
- struct logger_list* logger_list, log_id_t logId) {
+struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
struct android_log_logger_list* logger_list_internal =
(struct android_log_logger_list*)logger_list;
struct android_log_logger* logger;
@@ -284,7 +256,7 @@
}
}
- logger = calloc(1, sizeof(*logger));
+ logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
if (!logger) {
goto err;
}
@@ -311,8 +283,8 @@
}
/* Open the single named log and make it part of a new logger list */
-LIBLOG_ABI_PUBLIC struct logger_list* android_logger_list_open(
- log_id_t logId, int mode, unsigned int tail, pid_t pid) {
+struct logger_list* android_logger_list_open(log_id_t logId, int mode, unsigned int tail,
+ pid_t pid) {
struct logger_list* logger_list = android_logger_list_alloc(mode, tail, pid);
if (!logger_list) {
@@ -367,8 +339,7 @@
}
/* Read from the selected logs */
-LIBLOG_ABI_PUBLIC int android_logger_list_read(struct logger_list* logger_list,
- struct log_msg* log_msg) {
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
struct android_log_transport_context* transp;
struct android_log_logger_list* logger_list_internal =
(struct android_log_logger_list*)logger_list;
@@ -379,8 +350,8 @@
}
/* at least one transport */
- transp = node_to_item(logger_list_internal->transport.next,
- struct android_log_transport_context, node);
+ transp = node_to_item(logger_list_internal->transport.next, struct android_log_transport_context,
+ node);
/* more than one transport? */
if (transp->node.next != &logger_list_internal->transport) {
@@ -401,11 +372,9 @@
retval = transp->ret = 0;
} else if ((logger_list_internal->mode & ANDROID_LOG_NONBLOCK) ||
!transp->transport->poll) {
- retval = android_transport_read(logger_list_internal, transp,
- &transp->logMsg);
+ retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
} else {
- int pollval =
- (*transp->transport->poll)(logger_list_internal, transp);
+ int pollval = (*transp->transport->poll)(logger_list_internal, transp);
if (pollval <= 0) {
sched_yield();
pollval = (*transp->transport->poll)(logger_list_internal, transp);
@@ -417,8 +386,7 @@
}
retval = transp->ret = pollval;
} else if (pollval > 0) {
- retval = android_transport_read(logger_list_internal, transp,
- &transp->logMsg);
+ retval = android_transport_read(logger_list_internal, transp, &transp->logMsg);
}
}
}
@@ -431,11 +399,9 @@
(oldest->logMsg.entry.nsec > transp->logMsg.entry.nsec)))) {
oldest = transp;
}
- transp = node_to_item(transp->node.next,
- struct android_log_transport_context, node);
+ transp = node_to_item(transp->node.next, struct android_log_transport_context, node);
} while (transp != node_to_item(&logger_list_internal->transport,
- struct android_log_transport_context,
- node));
+ struct android_log_transport_context, node));
if (!oldest && (logger_list_internal->mode & ANDROID_LOG_NONBLOCK)) {
return (ret < 0) ? ret : -EAGAIN;
}
@@ -449,10 +415,10 @@
ret = oldest->ret;
if (ret < oldest->logMsg.entry.hdr_size) {
// zero truncated header fields.
- memset(log_msg, 0,
- (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg)
- ? sizeof(oldest->logMsg)
- : oldest->logMsg.entry.hdr_size));
+ memset(
+ log_msg, 0,
+ (oldest->logMsg.entry.hdr_size > sizeof(oldest->logMsg) ? sizeof(oldest->logMsg)
+ : oldest->logMsg.entry.hdr_size));
}
memcpy(log_msg, &oldest->logMsg, ret);
oldest->logMsg.entry.len = 0; /* Mark it as copied */
@@ -464,7 +430,7 @@
}
/* Close all the logs */
-LIBLOG_ABI_PUBLIC void android_logger_list_free(struct logger_list* logger_list) {
+void android_logger_list_free(struct logger_list* logger_list) {
struct android_log_logger_list* logger_list_internal =
(struct android_log_logger_list*)logger_list;
@@ -472,10 +438,6 @@
return;
}
- logger_list_wrlock();
- list_remove(&logger_list_internal->node);
- logger_list_unlock();
-
while (!list_empty(&logger_list_internal->transport)) {
struct listnode* node = list_head(&logger_list_internal->transport);
struct android_log_transport_context* transp =
@@ -490,8 +452,7 @@
while (!list_empty(&logger_list_internal->logger)) {
struct listnode* node = list_head(&logger_list_internal->logger);
- struct android_log_logger* logger =
- node_to_item(node, struct android_log_logger, node);
+ struct android_log_logger* logger = node_to_item(node, struct android_log_logger, node);
android_logger_free((struct logger*)logger);
}
diff --git a/liblog/logger_write.c b/liblog/logger_write.cpp
similarity index 76%
rename from liblog/logger_write.c
rename to liblog/logger_write.cpp
index 2754e6e..a4b3cd7 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.cpp
@@ -33,23 +33,19 @@
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
+#include "uio.h"
#define LOG_BUF_SIZE 1024
static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec* vec,
- size_t nr) = __write_to_log_init;
+static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
/*
* This is used by the C++ code to decide if it should write logs through
* the C code. Basically, if /dev/socket/logd is available, we're running in
* the simulator rather than a desktop tool and want to use the device.
*/
-static enum {
- kLogUninitialized,
- kLogNotAvailable,
- kLogAvailable
-} g_log_status = kLogUninitialized;
+static enum { kLogUninitialized, kLogNotAvailable, kLogAvailable } g_log_status = kLogUninitialized;
static int check_log_uid_permissions() {
#if defined(__ANDROID__)
@@ -70,7 +66,7 @@
if (num_groups <= 0) {
return -EPERM;
}
- groups = calloc(num_groups, sizeof(gid_t));
+ groups = static_cast<gid_t*>(calloc(num_groups, sizeof(gid_t)));
if (!groups) {
return -ENOMEM;
}
@@ -93,9 +89,8 @@
return 0;
}
-static void __android_log_cache_available(
- struct android_log_transport_write* node) {
- size_t i;
+static void __android_log_cache_available(struct android_log_transport_write* node) {
+ uint32_t i;
if (node->logMask) {
return;
@@ -104,13 +99,13 @@
for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
if (node->write && (i != LOG_ID_KERNEL) &&
((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
- (!node->available || ((*node->available)(i) >= 0))) {
+ (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
node->logMask |= 1 << i;
}
}
}
-LIBLOG_ABI_PUBLIC int __android_log_dev_available() {
+extern "C" int __android_log_dev_available() {
struct android_log_transport_write* node;
if (list_empty(&__android_log_transport_write)) {
@@ -133,7 +128,7 @@
/*
* Release any logger resources. A new log write will immediately re-acquire.
*/
-LIBLOG_ABI_PUBLIC void __android_log_close() {
+void __android_log_close() {
struct android_log_transport_write* transport;
#if defined(__ANDROID__)
EventTagMap* m;
@@ -308,10 +303,9 @@
}
}
if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
- tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
+ tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base)));
}
- ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
- ANDROID_LOG_VERBOSE);
+ ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE);
if (f) { /* local copy marked for close */
android_closeEventTagMap(f);
}
@@ -322,7 +316,7 @@
} else {
/* Validate the incoming tag, tag content can not split across iovec */
char prio = ANDROID_LOG_VERBOSE;
- const char* tag = vec[0].iov_base;
+ const char* tag = static_cast<const char*>(vec[0].iov_base);
size_t len = vec[0].iov_len;
if (!tag) {
len = 0;
@@ -408,67 +402,20 @@
return ret;
}
-LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
- const char* msg) {
+int __android_log_write(int prio, const char* tag, const char* msg) {
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
-LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
- const char* tag, const char* msg) {
- struct iovec vec[3];
- char tmp_tag[32];
-
+int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
if (!tag) tag = "";
- /* XXX: This needs to go! */
- if (bufID != LOG_ID_RADIO) {
- switch (tag[0]) {
- case 'H':
- if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
- goto inform;
- case 'R':
- /* Any log tag with "RIL" as the prefix */
- if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
- goto inform;
- case 'Q':
- /* Any log tag with "QC_RIL" as the prefix */
- if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
- goto inform;
- case 'I':
- /* Any log tag with "IMS" as the prefix */
- if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
- goto inform;
- case 'A':
- if (strcmp(tag + 1, "AT" + 1)) break;
- goto inform;
- case 'G':
- if (strcmp(tag + 1, "GSM" + 1)) break;
- goto inform;
- case 'S':
- if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
- goto inform;
- case 'C':
- if (strcmp(tag + 1, "CDMA" + 1)) break;
- goto inform;
- case 'P':
- if (strcmp(tag + 1, "PHONE" + 1)) break;
- /* FALLTHRU */
- inform:
- bufID = LOG_ID_RADIO;
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
- /* FALLTHRU */
- default:
- break;
- }
- }
-
#if __BIONIC__
if (prio == ANDROID_LOG_FATAL) {
android_set_abort_message(msg);
}
#endif
+ struct iovec vec[3];
vec[0].iov_base = (unsigned char*)&prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void*)tag;
@@ -476,11 +423,10 @@
vec[2].iov_base = (void*)msg;
vec[2].iov_len = strlen(msg) + 1;
- return write_to_log(bufID, vec, 3);
+ return write_to_log(static_cast<log_id_t>(bufID), vec, 3);
}
-LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char* tag,
- const char* fmt, va_list ap) {
+int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) {
char buf[LOG_BUF_SIZE];
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
@@ -488,8 +434,7 @@
return __android_log_write(prio, tag, buf);
}
-LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char* tag,
- const char* fmt, ...) {
+int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -500,9 +445,7 @@
return __android_log_write(prio, tag, buf);
}
-LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
- const char* tag, const char* fmt,
- ...) {
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -513,8 +456,7 @@
return __android_log_buf_write(bufID, prio, tag, buf);
}
-LIBLOG_ABI_PUBLIC void __android_log_assert(const char* cond, const char* tag,
- const char* fmt, ...) {
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...) {
char buf[LOG_BUF_SIZE];
if (fmt) {
@@ -535,18 +477,15 @@
// Log assertion failures to stderr for the benefit of "adb shell" users
// and gtests (http://b/23675822).
- struct iovec iov[2] = {
- { buf, strlen(buf) }, { (char*)"\n", 1 },
- };
- TEMP_FAILURE_RETRY(writev(2, iov, 2));
+ TEMP_FAILURE_RETRY(write(2, buf, strlen(buf)));
+ TEMP_FAILURE_RETRY(write(2, "\n", 1));
__android_log_write(ANDROID_LOG_FATAL, tag, buf);
abort(); /* abort so we have a chance to debug the situation */
/* NOTREACHED */
}
-LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag, const void* payload,
- size_t len) {
+int __android_log_bwrite(int32_t tag, const void* payload, size_t len) {
struct iovec vec[2];
vec[0].iov_base = &tag;
@@ -557,9 +496,7 @@
return write_to_log(LOG_ID_EVENTS, vec, 2);
}
-LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
- const void* payload,
- size_t len) {
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len) {
struct iovec vec[2];
vec[0].iov_base = &tag;
@@ -570,9 +507,7 @@
return write_to_log(LOG_ID_STATS, vec, 2);
}
-LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
- const void* payload,
- size_t len) {
+int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len) {
struct iovec vec[2];
vec[0].iov_base = &tag;
@@ -588,8 +523,7 @@
* for the general case where we're generating lists of stuff, but very
* handy if we just want to dump an integer into the log.
*/
-LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
- const void* payload, size_t len) {
+int __android_log_btwrite(int32_t tag, char type, const void* payload, size_t len) {
struct iovec vec[3];
vec[0].iov_base = &tag;
@@ -606,7 +540,7 @@
* Like __android_log_bwrite, but used for writing strings to the
* event log.
*/
-LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char* payload) {
+int __android_log_bswrite(int32_t tag, const char* payload) {
struct iovec vec[4];
char type = EVENT_TYPE_STRING;
uint32_t len = strlen(payload);
@@ -627,8 +561,7 @@
* Like __android_log_security_bwrite, but used for writing strings to the
* security log.
*/
-LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
- const char* payload) {
+int __android_log_security_bswrite(int32_t tag, const char* payload) {
struct iovec vec[4];
char type = EVENT_TYPE_STRING;
uint32_t len = strlen(payload);
@@ -663,9 +596,9 @@
/* Following functions need access to our internal write_to_log status */
-LIBLOG_HIDDEN int __android_log_transport;
+int __android_log_transport;
-LIBLOG_ABI_PUBLIC int android_set_log_transport(int transport_flag) {
+int android_set_log_transport(int transport_flag) {
int retval;
if (transport_flag < 0) {
@@ -684,9 +617,9 @@
return retval;
}
- __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+ __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
- transport_flag &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+ transport_flag &= LOGGER_LOGD | LOGGER_STDERR;
if (__android_log_transport != transport_flag) {
__android_log_transport = transport_flag;
@@ -695,8 +628,7 @@
write_to_log = __write_to_log_init;
/* generically we only expect these two values for write_to_log */
- } else if ((write_to_log != __write_to_log_init) &&
- (write_to_log != __write_to_log_daemon)) {
+ } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
write_to_log = __write_to_log_init;
}
@@ -707,17 +639,16 @@
return retval;
}
-LIBLOG_ABI_PUBLIC int android_get_log_transport() {
+int android_get_log_transport() {
int ret = LOGGER_DEFAULT;
__android_log_lock();
if (write_to_log == __write_to_log_null) {
ret = LOGGER_NULL;
} else {
- __android_log_transport &= LOGGER_LOCAL | LOGGER_LOGD | LOGGER_STDERR;
+ __android_log_transport &= LOGGER_LOGD | LOGGER_STDERR;
ret = __android_log_transport;
- if ((write_to_log != __write_to_log_init) &&
- (write_to_log != __write_to_log_daemon)) {
+ if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
ret = -EINVAL;
}
}
diff --git a/liblog/logprint.c b/liblog/logprint.cpp
similarity index 85%
rename from liblog/logprint.c
rename to liblog/logprint.cpp
index 7937cb1..6b5ea4c 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.cpp
@@ -15,7 +15,6 @@
** limitations under the License.
*/
-#define _GNU_SOURCE /* for asprintf */
#ifndef __MINGW32__
#define HAVE_STRSEP
#endif
@@ -34,6 +33,7 @@
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
+#include <wchar.h>
#include <cutils/list.h>
#include <log/log.h>
@@ -154,7 +154,7 @@
case ANDROID_LOG_DEFAULT:
case ANDROID_LOG_UNKNOWN:
default: return '?';
- /* clang-format on */
+ /* clang-format on */
}
}
@@ -172,16 +172,14 @@
case ANDROID_LOG_DEFAULT:
case ANDROID_LOG_UNKNOWN:
default: return ANDROID_COLOR_DEFAULT;
- /* clang-format on */
+ /* clang-format on */
}
}
-static android_LogPriority filterPriForTag(AndroidLogFormat* p_format,
- const char* tag) {
+static android_LogPriority filterPriForTag(AndroidLogFormat* p_format, const char* tag) {
FilterInfo* p_curFilter;
- for (p_curFilter = p_format->filters; p_curFilter != NULL;
- p_curFilter = p_curFilter->p_next) {
+ for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next) {
if (0 == strcmp(tag, p_curFilter->mTag)) {
if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) {
return p_format->global_pri;
@@ -198,16 +196,15 @@
* returns 1 if this log line should be printed based on its priority
* and tag, and 0 if it should not
*/
-LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine(AndroidLogFormat* p_format,
- const char* tag,
- android_LogPriority pri) {
+int android_log_shouldPrintLine(AndroidLogFormat* p_format, const char* tag,
+ android_LogPriority pri) {
return pri >= filterPriForTag(p_format, tag);
}
-LIBLOG_ABI_PUBLIC AndroidLogFormat* android_log_format_new() {
+AndroidLogFormat* android_log_format_new() {
AndroidLogFormat* p_ret;
- p_ret = calloc(1, sizeof(AndroidLogFormat));
+ p_ret = static_cast<AndroidLogFormat*>(calloc(1, sizeof(AndroidLogFormat)));
p_ret->global_pri = ANDROID_LOG_VERBOSE;
p_ret->format = FORMAT_BRIEF;
@@ -232,7 +229,7 @@
static list_declare(convertHead);
-LIBLOG_ABI_PUBLIC void android_log_format_free(AndroidLogFormat* p_format) {
+void android_log_format_free(AndroidLogFormat* p_format) {
FilterInfo *p_info, *p_info_old;
p_info = p_format->filters;
@@ -255,8 +252,7 @@
}
}
-LIBLOG_ABI_PUBLIC int android_log_setPrintFormat(AndroidLogFormat* p_format,
- AndroidLogPrintFormat format) {
+int android_log_setPrintFormat(AndroidLogFormat* p_format, AndroidLogPrintFormat format) {
switch (format) {
case FORMAT_MODIFIER_COLOR:
p_format->colored_output = true;
@@ -302,8 +298,7 @@
/**
* Returns FORMAT_OFF on invalid string
*/
-LIBLOG_ABI_PUBLIC AndroidLogPrintFormat
-android_log_formatFromString(const char* formatString) {
+AndroidLogPrintFormat android_log_formatFromString(const char* formatString) {
static AndroidLogPrintFormat format;
/* clang-format off */
@@ -326,7 +321,7 @@
else if (!strcmp(formatString, "monotonic")) format = FORMAT_MODIFIER_MONOTONIC;
else if (!strcmp(formatString, "uid")) format = FORMAT_MODIFIER_UID;
else if (!strcmp(formatString, "descriptive")) format = FORMAT_MODIFIER_DESCRIPT;
- /* clang-format on */
+ /* clang-format on */
#ifndef __MINGW32__
else {
@@ -344,9 +339,8 @@
*/
tzset();
if (!tzname[0] ||
- ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
- && strcasecmp(formatString, utc) &&
- strcasecmp(formatString, gmt))) { /* ok */
+ ((!strcmp(tzname[0], utc) || !strcmp(tzname[0], gmt)) /* error? */
+ && strcasecmp(formatString, utc) && strcasecmp(formatString, gmt))) { /* ok */
if (cp) {
setenv(tz, cp, 1);
} else {
@@ -373,8 +367,7 @@
* Assumes single threaded execution
*/
-LIBLOG_ABI_PUBLIC int android_log_addFilterRule(AndroidLogFormat* p_format,
- const char* filterExpression) {
+int android_log_addFilterRule(AndroidLogFormat* p_format, const char* filterExpression) {
size_t tagNameLength;
android_LogPriority pri = ANDROID_LOG_DEFAULT;
@@ -469,8 +462,7 @@
* Assumes single threaded execution
*
*/
-LIBLOG_ABI_PUBLIC int android_log_addFilterString(AndroidLogFormat* p_format,
- const char* filterString) {
+int android_log_addFilterString(AndroidLogFormat* p_format, const char* filterString) {
char* filterStringCopy = strdup(filterString);
char* p_cur = filterStringCopy;
char* p_ret;
@@ -502,8 +494,7 @@
* Returns 0 on success and -1 on invalid wire format (entry will be
* in unspecified state)
*/
-LIBLOG_ABI_PUBLIC int android_log_processLogBuffer(struct logger_entry* buf,
- AndroidLogEntry* entry) {
+int android_log_processLogBuffer(struct logger_entry* buf, AndroidLogEntry* entry) {
entry->message = NULL;
entry->messageLen = 0;
@@ -581,7 +572,7 @@
msg[msgEnd] = '\0';
}
- entry->priority = msg[0];
+ entry->priority = static_cast<android_LogPriority>(msg[0]);
entry->tag = msg + 1;
entry->tagLen = msgStart - 1;
entry->message = msg + msgStart;
@@ -643,9 +634,8 @@
TYPE_MONOTONIC = 's'
};
-static int android_log_printBinaryEvent(const unsigned char** pEventData,
- size_t* pEventDataLen, char** pOutBuf,
- size_t* pOutBufLen, const char** fmtStr,
+static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen,
+ char** pOutBuf, size_t* pOutBufLen, const char** fmtStr,
size_t* fmtLen) {
const unsigned char* eventData = *pEventData;
size_t eventDataLen = *pEventDataLen;
@@ -729,13 +719,10 @@
}
if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
- static const unsigned char typeTable[] = {
- EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING, EVENT_TYPE_LIST,
- EVENT_TYPE_FLOAT
- };
+ static const unsigned char typeTable[] = {EVENT_TYPE_INT, EVENT_TYPE_LONG, EVENT_TYPE_STRING,
+ EVENT_TYPE_LIST, EVENT_TYPE_FLOAT};
- if ((*cp >= '1') &&
- (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+ if ((*cp >= '1') && (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
(type != typeTable[(size_t)(*cp - '1')]))
len = 0;
@@ -858,8 +845,8 @@
outBufLen--;
for (i = 0; i < count; i++) {
- result = android_log_printBinaryEvent(
- &eventData, &eventDataLen, &outBuf, &outBufLen, fmtStr, fmtLen);
+ result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
+ fmtStr, fmtLen);
if (result != 0) goto bail;
if (i < (count - 1)) {
@@ -889,24 +876,21 @@
case TYPE_BYTES:
if ((lval != 0) && ((lval % 1024) == 0)) {
/* repaint with multiplier */
- static const char suffixTable[] = { 'K', 'M', 'G', 'T' };
+ static const char suffixTable[] = {'K', 'M', 'G', 'T'};
size_t idx = 0;
outBuf -= outCount;
outBufLen += outCount;
do {
lval /= 1024;
if ((lval % 1024) != 0) break;
- } while (++idx <
- ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
- outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval,
- suffixTable[idx]);
+ } while (++idx < ((sizeof(suffixTable) / sizeof(suffixTable[0])) - 1));
+ outCount = snprintf(outBuf, outBufLen, "%" PRId64 "%cB", lval, suffixTable[idx]);
} else {
outCount = snprintf(outBuf, outBufLen, "B");
}
break;
case TYPE_MILLISECONDS:
- if (((lval <= -1000) || (1000 <= lval)) &&
- (outBufLen || (outBuf[-1] == '0'))) {
+ if (((lval <= -1000) || (1000 <= lval)) && (outBufLen || (outBuf[-1] == '0'))) {
/* repaint as (fractional) seconds, possibly saving space */
if (outBufLen) outBuf[0] = outBuf[-1];
outBuf[-1] = outBuf[-2];
@@ -943,22 +927,19 @@
}
if (val >= minute) {
if (val >= hour) {
- outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":",
- (val / hour) % (day / hour));
+ outCount = snprintf(outBuf, outBufLen, "%" PRIu64 ":", (val / hour) % (day / hour));
if (outCount >= outBufLen) break;
outBuf += outCount;
outBufLen -= outCount;
}
outCount =
- snprintf(outBuf, outBufLen,
- (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ snprintf(outBuf, outBufLen, (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
(val / minute) % (hour / minute));
if (outCount >= outBufLen) break;
outBuf += outCount;
outBufLen -= outCount;
}
- outCount = snprintf(outBuf, outBufLen,
- (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
+ outCount = snprintf(outBuf, outBufLen, (val >= minute) ? "%02" PRIu64 : "%" PRIu64 "s",
val % minute);
} break;
case TYPE_ALLOCATIONS:
@@ -1013,9 +994,9 @@
* it however we choose, which means we can't really use a fixed-size buffer
* here.
*/
-LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
+int android_log_processBinaryLogBuffer(
struct logger_entry* buf, AndroidLogEntry* entry,
- const EventTagMap* map __unused, /* only on !__ANDROID__ */
+ [[maybe_unused]] const EventTagMap* map, /* only on !__ANDROID__ */
char* messageBuf, int messageBufLen) {
size_t inCount;
uint32_t tagIndex;
@@ -1100,8 +1081,8 @@
int result = 0;
if ((inCount > 0) || fmtLen) {
- result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
- &outRemaining, &fmtStr, &fmtLen);
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, &fmtStr,
+ &fmtLen);
}
if ((result == 1) && fmtStr) {
/* We overflowed :-(, let's repaint the line w/o format dressings */
@@ -1112,8 +1093,7 @@
eventData += 4;
outBuf = messageBuf;
outRemaining = messageBufLen - 1;
- result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
- &outRemaining, NULL, NULL);
+ result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining, NULL, NULL);
}
if (result < 0) {
fprintf(stderr, "Binary log entry conversion failed\n");
@@ -1155,67 +1135,13 @@
}
/*
- * One utf8 character at a time
- *
- * Returns the length of the utf8 character in the buffer,
- * or -1 if illegal or truncated
- *
- * Open coded from libutils/Unicode.cpp, borrowed from utf8_length(),
- * can not remove from here because of library circular dependencies.
- * Expect one-day utf8_character_length with the same signature could
- * _also_ be part of libutils/Unicode.cpp if its usefullness needs to
- * propagate globally.
- */
-LIBLOG_WEAK ssize_t utf8_character_length(const char* src, size_t len) {
- const char* cur = src;
- const char first_char = *cur++;
- static const uint32_t kUnicodeMaxCodepoint = 0x0010FFFF;
- int32_t mask, to_ignore_mask;
- size_t num_to_read;
- uint32_t utf32;
-
- if ((first_char & 0x80) == 0) { /* ASCII */
- return first_char ? 1 : -1;
- }
-
- /*
- * (UTF-8's character must not be like 10xxxxxx,
- * but 110xxxxx, 1110xxxx, ... or 1111110x)
- */
- if ((first_char & 0x40) == 0) {
- return -1;
- }
-
- for (utf32 = 1, num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
- num_to_read < 5 && (first_char & mask);
- num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- if (num_to_read > len) {
- return -1;
- }
- if ((*cur & 0xC0) != 0x80) { /* can not be 10xxxxxx? */
- return -1;
- }
- utf32 = (utf32 << 6) + (*cur++ & 0b00111111);
- }
- /* "first_char" must be (110xxxxx - 11110xxx) */
- if (num_to_read >= 5) {
- return -1;
- }
- to_ignore_mask |= mask;
- utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
- if (utf32 > kUnicodeMaxCodepoint) {
- return -1;
- }
- return num_to_read;
-}
-
-/*
* Convert to printable from message to p buffer, return string length. If p is
* NULL, do not copy, but still return the expected string length.
*/
-static size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+size_t convertPrintable(char* p, const char* message, size_t messageLen) {
char* begin = p;
bool print = p != NULL;
+ mbstate_t mb_state = {};
while (messageLen) {
char buf[6];
@@ -1223,12 +1149,10 @@
if ((size_t)len > messageLen) {
len = messageLen;
}
- len = utf8_character_length(message, len);
+ len = mbrtowc(nullptr, message, len, &mb_state);
if (len < 0) {
- snprintf(buf, sizeof(buf),
- ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
- *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
len = 1;
} else {
buf[0] = '\0';
@@ -1248,7 +1172,7 @@
} else if (*message == '\\') {
strcpy(buf, "\\\\");
} else if ((*message < ' ') || (*message & 0x80)) {
- snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
}
}
if (!buf[0]) {
@@ -1281,8 +1205,7 @@
return p;
}
-static struct timespec* sumTimespec(struct timespec* left,
- struct timespec* right) {
+static struct timespec* sumTimespec(struct timespec* left, struct timespec* right) {
left->tv_nsec += right->tv_nsec;
left->tv_sec += right->tv_sec;
if (left->tv_nsec >= (long)NS_PER_SEC) {
@@ -1292,8 +1215,7 @@
return left;
}
-static struct timespec* subTimespec(struct timespec* result,
- struct timespec* left,
+static struct timespec* subTimespec(struct timespec* result, struct timespec* left,
struct timespec* right) {
result->tv_nsec = left->tv_nsec - right->tv_nsec;
result->tv_sec = left->tv_sec - right->tv_sec;
@@ -1309,8 +1231,7 @@
}
#ifdef __ANDROID__
-static void convertMonotonic(struct timespec* result,
- const AndroidLogEntry* entry) {
+static void convertMonotonic(struct timespec* result, const AndroidLogEntry* entry) {
struct listnode* node;
struct conversionList {
struct listnode node; /* first */
@@ -1322,8 +1243,8 @@
/* If we do not have a conversion list, build one up */
if (list_empty(&convertHead)) {
bool suspended_pending = false;
- struct timespec suspended_monotonic = { 0, 0 };
- struct timespec suspended_diff = { 0, 0 };
+ struct timespec suspended_monotonic = {0, 0};
+ struct timespec suspended_diff = {0, 0};
/*
* Read dmesg for _some_ synchronization markers and insert
@@ -1413,15 +1334,14 @@
} else {
unsetenv(tz);
}
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
list->time = time;
subTimespec(&list->convert, &time, &monotonic);
list_add_tail(&convertHead, &list->node);
}
if (suspended_pending && !list_empty(&convertHead)) {
- list = node_to_item(list_tail(&convertHead), struct conversionList,
- node);
+ list = node_to_item(list_tail(&convertHead), struct conversionList, node);
if (subTimespec(&time, subTimespec(&time, &list->time, &list->convert),
&suspended_monotonic)
->tv_sec > 0) {
@@ -1434,13 +1354,13 @@
time = suspended_monotonic;
sumTimespec(&time, &convert);
/* breakpoint just before sleep */
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
list->time = time;
list->convert = convert;
list_add_tail(&convertHead, &list->node);
/* breakpoint just after sleep */
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
list->time = time;
sumTimespec(&list->time, &suspended_diff);
@@ -1453,7 +1373,7 @@
pclose(p);
}
/* last entry is our current time conversion */
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
clock_gettime(CLOCK_REALTIME, &list->time);
clock_gettime(CLOCK_MONOTONIC, &convert);
@@ -1469,7 +1389,7 @@
time = suspended_monotonic;
sumTimespec(&time, &convert);
/* breakpoint just after sleep */
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
list->time = time;
sumTimespec(&list->time, &suspended_diff);
@@ -1477,7 +1397,7 @@
sumTimespec(&list->convert, &suspended_diff);
list_add_head(&convertHead, &list->node);
/* breakpoint just before sleep */
- list = calloc(1, sizeof(struct conversionList));
+ list = static_cast<conversionList*>(calloc(1, sizeof(conversionList)));
list_init(&list->node);
list->time = time;
list->convert = convert;
@@ -1548,11 +1468,9 @@
* Returns NULL on malloc error
*/
-LIBLOG_ABI_PUBLIC char* android_log_formatLogLine(AndroidLogFormat* p_format,
- char* defaultBuffer,
- size_t defaultBufferSize,
- const AndroidLogEntry* entry,
- size_t* p_outLength) {
+char* android_log_formatLogLine(AndroidLogFormat* p_format, char* defaultBuffer,
+ size_t defaultBufferSize, const AndroidLogEntry* entry,
+ size_t* p_outLength) {
#if !defined(_WIN32)
struct tm tmBuf;
#endif
@@ -1600,26 +1518,23 @@
}
if (p_format->epoch_output || p_format->monotonic_output) {
ptm = NULL;
- snprintf(timeBuf, sizeof(timeBuf),
- p_format->monotonic_output ? "%6lld" : "%19lld", (long long)now);
+ snprintf(timeBuf, sizeof(timeBuf), p_format->monotonic_output ? "%6lld" : "%19lld",
+ (long long)now);
} else {
#if !defined(_WIN32)
ptm = localtime_r(&now, &tmBuf);
#else
ptm = localtime(&now);
#endif
- strftime(timeBuf, sizeof(timeBuf),
- &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
+ strftime(timeBuf, sizeof(timeBuf), &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3], ptm);
}
len = strlen(timeBuf);
if (p_format->nsec_time_output) {
len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%09ld", nsec);
} else if (p_format->usec_time_output) {
- len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld",
- nsec / US_PER_NSEC);
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%06ld", nsec / US_PER_NSEC);
} else {
- len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld",
- nsec / MS_PER_NSEC);
+ len += snprintf(timeBuf + len, sizeof(timeBuf) - len, ".%03ld", nsec / MS_PER_NSEC);
}
if (p_format->zone_output && ptm) {
strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
@@ -1629,8 +1544,8 @@
* Construct a buffer containing the log header and log message.
*/
if (p_format->colored_output) {
- prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
- colorFromPri(entry->priority));
+ prefixLen =
+ snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm", colorFromPri(entry->priority));
prefixLen = MIN(prefixLen, sizeof(prefixBuf));
const char suffixContents[] = "\x1B[0m";
@@ -1649,8 +1564,7 @@
#if !defined(__MINGW32__)
#if (FAKE_LOG_DEVICE == 0)
#ifndef __BIONIC__
-#warning \
- "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
#endif
#endif
struct passwd* pwd = getpwuid(entry->uid);
@@ -1669,21 +1583,21 @@
switch (p_format->format) {
case FORMAT_TAG:
- len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c/%-8.*s: ", priChar, (int)entry->tagLen, entry->tag);
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c/%-8.*s: ", priChar,
+ (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_PROCESS:
- len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen,
- " (%.*s)\n", (int)entry->tagLen, entry->tag);
+ len = snprintf(suffixBuf + suffixLen, sizeof(suffixBuf) - suffixLen, " (%.*s)\n",
+ (int)entry->tagLen, entry->tag);
suffixLen += MIN(len, sizeof(suffixBuf) - suffixLen);
- len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%s%5d) ", priChar, uid, entry->pid);
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d) ", priChar,
+ uid, entry->pid);
break;
case FORMAT_THREAD:
- len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c(%s%5d:%5d) ", priChar, uid, entry->pid, entry->tid);
+ len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen, "%c(%s%5d:%5d) ",
+ priChar, uid, entry->pid, entry->tid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -1695,8 +1609,8 @@
break;
case FORMAT_TIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar,
- (int)entry->tagLen, entry->tag, uid, entry->pid);
+ "%s %c/%-8.*s(%s%5d): ", timeBuf, priChar, (int)entry->tagLen, entry->tag, uid,
+ entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -1706,24 +1620,24 @@
*ret = ' ';
}
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid,
- entry->tid, priChar, (int)entry->tagLen, entry->tag);
+ "%s %s%5d %5d %c %-8.*s: ", timeBuf, uid, entry->pid, entry->tid, priChar,
+ (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
case FORMAT_LONG:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid,
- entry->tid, priChar, (int)entry->tagLen, entry->tag);
+ "[ %s %s%5d:%5d %c/%-8.*s ]\n", timeBuf, uid, entry->pid, entry->tid, priChar,
+ (int)entry->tagLen, entry->tag);
strcpy(suffixBuf + suffixLen, "\n\n");
suffixLen += 2;
prefixSuffixIsHeaderFooter = 1;
break;
case FORMAT_BRIEF:
default:
- len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
- "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen,
- entry->tag, uid, entry->pid);
+ len =
+ snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
+ "%c/%-8.*s(%s%5d): ", priChar, (int)entry->tagLen, entry->tag, uid, entry->pid);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
@@ -1847,16 +1761,14 @@
* Returns count bytes written
*/
-LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format,
- int fd,
- const AndroidLogEntry* entry) {
+int android_log_printLogLine(AndroidLogFormat* p_format, int fd, const AndroidLogEntry* entry) {
int ret;
char defaultBuffer[512];
char* outBuffer = NULL;
size_t totalLen;
- outBuffer = android_log_formatLogLine(
- p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
+ outBuffer =
+ android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
if (!outBuffer) return -1;
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.cpp
similarity index 83%
rename from liblog/pmsg_reader.c
rename to liblog/pmsg_reader.cpp
index c3ed8a2..ba27fd7 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.cpp
@@ -32,28 +32,27 @@
static int pmsgVersion(struct android_log_logger* logger,
struct android_log_transport_context* transp);
static int pmsgRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg);
+ struct android_log_transport_context* transp, struct log_msg* log_msg);
static void pmsgClose(struct android_log_logger_list* logger_list,
struct android_log_transport_context* transp);
static int pmsgClear(struct android_log_logger* logger,
struct android_log_transport_context* transp);
-LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
- .node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
- .name = "pmsg",
- .available = pmsgAvailable,
- .version = pmsgVersion,
- .read = pmsgRead,
- .poll = NULL,
- .close = pmsgClose,
- .clear = pmsgClear,
- .setSize = NULL,
- .getSize = NULL,
- .getReadableSize = NULL,
- .getPrune = NULL,
- .setPrune = NULL,
- .getStats = NULL,
+struct android_log_transport_read pmsgLoggerRead = {
+ .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
+ .name = "pmsg",
+ .available = pmsgAvailable,
+ .version = pmsgVersion,
+ .read = pmsgRead,
+ .poll = NULL,
+ .close = pmsgClose,
+ .clear = pmsgClear,
+ .setSize = NULL,
+ .getSize = NULL,
+ .getReadableSize = NULL,
+ .getPrune = NULL,
+ .setPrune = NULL,
+ .getStats = NULL,
};
static int pmsgAvailable(log_id_t logId) {
@@ -68,8 +67,7 @@
/* Determine the credentials of the caller */
static bool uid_has_log_permission(uid_t uid) {
- return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) ||
- (uid == AID_LOGD);
+ return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
}
static uid_t get_best_effective_uid() {
@@ -130,8 +128,7 @@
}
static int pmsgRead(struct android_log_logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
+ struct android_log_transport_context* transp, struct log_msg* log_msg) {
ssize_t ret;
off_t current, next;
uid_t uid;
@@ -174,8 +171,7 @@
if (fd <= 0) {
return -EBADF;
}
- ret = TEMP_FAILURE_RETRY(
- read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
+ ret = TEMP_FAILURE_RETRY(read(fd, &buf.p.magic + preread_count, sizeof(buf) - preread_count));
if (ret < 0) {
return -errno;
}
@@ -185,11 +181,10 @@
return preread_count ? -EIO : -EAGAIN;
}
if ((buf.p.magic != LOGGER_MAGIC) || (buf.p.len <= sizeof(buf)) ||
- (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) ||
- (buf.l.id >= LOG_ID_MAX) || (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
+ (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD)) || (buf.l.id >= LOG_ID_MAX) ||
+ (buf.l.realtime.tv_nsec >= NS_PER_SEC) ||
((buf.l.id != LOG_ID_EVENTS) && (buf.l.id != LOG_ID_SECURITY) &&
- ((buf.prio == ANDROID_LOG_UNKNOWN) ||
- (buf.prio == ANDROID_LOG_DEFAULT) ||
+ ((buf.prio == ANDROID_LOG_UNKNOWN) || (buf.prio == ANDROID_LOG_DEFAULT) ||
(buf.prio >= ANDROID_LOG_SILENT)))) {
do {
memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
@@ -213,8 +208,7 @@
if (fd <= 0) {
return -EBADF;
}
- ret = TEMP_FAILURE_RETRY(
- read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+ ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
if (ret < 0) {
return -errno;
}
@@ -250,8 +244,7 @@
if (fd <= 0) {
return -EBADF;
}
- next = TEMP_FAILURE_RETRY(
- lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
+ next = TEMP_FAILURE_RETRY(lseek(fd, (off_t)(buf.p.len - sizeof(buf)), SEEK_CUR));
if (next < 0) {
return -errno;
}
@@ -269,9 +262,16 @@
}
}
-LIBLOG_ABI_PRIVATE ssize_t
-__android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
- __android_log_pmsg_file_read_fn fn, void* arg) {
+static void* realloc_or_free(void* ptr, size_t new_size) {
+ void* result = realloc(ptr, new_size);
+ if (!result) {
+ free(ptr);
+ }
+ return result;
+}
+
+ssize_t __android_log_pmsg_file_read(log_id_t logId, char prio, const char* prefix,
+ __android_log_pmsg_file_read_fn fn, void* arg) {
ssize_t ret;
struct android_log_logger_list logger_list;
struct android_log_transport_context transp;
@@ -304,14 +304,12 @@
memset(&logger_list, 0, sizeof(logger_list));
memset(&transp, 0, sizeof(transp));
- logger_list.mode =
- ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
+ logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
transp.logMask = (unsigned)-1;
if (logId != LOG_ID_ANY) {
transp.logMask = (1 << logId);
}
- transp.logMask &=
- ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+ transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
if (!transp.logMask) {
return -EINVAL;
}
@@ -338,15 +336,13 @@
/* Read the file content */
while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
- char* cp;
- size_t hdr_size = transp.logMsg.entry.hdr_size
- ? transp.logMsg.entry.hdr_size
- : sizeof(transp.logMsg.entry_v1);
+ const char* cp;
+ size_t hdr_size = transp.logMsg.entry.hdr_size ? transp.logMsg.entry.hdr_size
+ : sizeof(transp.logMsg.entry_v1);
char* msg = (char*)&transp.logMsg + hdr_size;
- char* split = NULL;
+ const char* split = NULL;
- if ((hdr_size < sizeof(transp.logMsg.entry_v1)) ||
- (hdr_size > sizeof(transp.logMsg.entry))) {
+ if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
continue;
}
/* Check for invalid sequence number */
@@ -358,8 +354,7 @@
/* Determine if it has <dirbase>:<filebase> format for tag */
len = transp.logMsg.entry.len - sizeof(prio);
- for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len;
- ++cp) {
+ for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
if (*cp == ':') {
if (split) {
break;
@@ -387,13 +382,12 @@
continue;
}
offset = split - prefix;
- if ((msg[offset + sizeof(prio)] != ':') ||
- strncmp(msg + sizeof(prio), prefix, offset)) {
+ if ((msg[offset + sizeof(prio)] != ':') || strncmp(msg + sizeof(prio), prefix, offset)) {
continue;
}
++offset;
- if ((prefix_len > offset) && strncmp(&msg[offset + sizeof(prio)],
- split + 1, prefix_len - offset)) {
+ if ((prefix_len > offset) &&
+ strncmp(&msg[offset + sizeof(prio)], split + 1, prefix_len - offset)) {
continue;
}
}
@@ -405,8 +399,8 @@
/* check if there is an existing entry */
list_for_each(node, &name_list) {
names = node_to_item(node, struct names, node);
- if (!strcmp(names->name, msg + sizeof(prio)) &&
- (names->id == transp.logMsg.entry.lid) && (names->prio == *msg)) {
+ if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
+ (names->prio == *msg)) {
break;
}
}
@@ -417,13 +411,13 @@
unsigned long long nl;
len = strlen(msg + sizeof(prio)) + 1;
- names = calloc(1, sizeof(*names) + len);
+ names = static_cast<struct names*>(calloc(1, sizeof(*names) + len));
if (!names) {
ret = -ENOMEM;
break;
}
strcpy(names->name, msg + sizeof(prio));
- names->id = transp.logMsg.entry.lid;
+ names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
names->prio = *msg;
list_init(&names->content);
/*
@@ -483,19 +477,17 @@
}
/* Add content */
- content =
- calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len);
+ content = static_cast<struct content*>(
+ calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
if (!content) {
ret = -ENOMEM;
break;
}
- memcpy(&content->entry, &transp.logMsg.entry,
- hdr_size + transp.logMsg.entry.len);
+ memcpy(&content->entry, &transp.logMsg.entry, hdr_size + transp.logMsg.entry.len);
/* Insert in sequence number sorted order, to ease reconstruction */
list_for_each_reverse(node, &names->content) {
- if ((node_to_item(node, struct content, node))->entry.nsec <
- transp.logMsg.entry.nsec) {
+ if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
break;
}
}
@@ -528,7 +520,7 @@
}
if (!buf) {
- buf = malloc(sizeof(char));
+ buf = static_cast<char*>(malloc(sizeof(char)));
if (!buf) {
ret = -ENOMEM;
list_remove(content_node);
@@ -541,7 +533,7 @@
/* Missing sequence numbers */
while (sequence < content->entry.nsec) {
/* plus space for enforced nul */
- buf = realloc(buf, len + sizeof(char) + sizeof(char));
+ buf = static_cast<char*>(realloc_or_free(buf, len + sizeof(char) + sizeof(char)));
if (!buf) {
break;
}
@@ -556,16 +548,14 @@
continue;
}
/* plus space for enforced nul */
- buf = realloc(buf, len + add_len + sizeof(char));
+ buf = static_cast<char*>(realloc_or_free(buf, len + add_len + sizeof(char)));
if (!buf) {
ret = -ENOMEM;
list_remove(content_node);
free(content);
continue;
}
- memcpy(buf + len,
- (char*)&content->entry + content->entry.hdr_size + tag_len +
- sizeof(prio),
+ memcpy(buf + len, (char*)&content->entry + content->entry.hdr_size + tag_len + sizeof(prio),
add_len);
len += add_len;
buf[len] = '\0'; /* enforce trailing hidden nul */
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.cpp
similarity index 84%
rename from liblog/pmsg_writer.c
rename to liblog/pmsg_writer.cpp
index dc42856..e851100 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.cpp
@@ -33,21 +33,21 @@
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
+#include "uio.h"
static int pmsgOpen();
static void pmsgClose();
static int pmsgAvailable(log_id_t logId);
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr);
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
-LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
- .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
- .context.fd = -1,
- .name = "pmsg",
- .available = pmsgAvailable,
- .open = pmsgOpen,
- .close = pmsgClose,
- .write = pmsgWrite,
+struct android_log_transport_write pmsgLoggerWrite = {
+ .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
+ .context.fd = -1,
+ .name = "pmsg",
+ .available = pmsgAvailable,
+ .open = pmsgOpen,
+ .close = pmsgClose,
+ .write = pmsgWrite,
};
static int pmsgOpen() {
@@ -76,8 +76,7 @@
if (logId > LOG_ID_SECURITY) {
return -EINVAL;
}
- if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) &&
- !__android_log_is_debuggable()) {
+ if ((logId != LOG_ID_SECURITY) && (logId != LOG_ID_EVENTS) && !__android_log_is_debuggable()) {
return -EINVAL;
}
if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) {
@@ -96,8 +95,7 @@
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
-static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr) {
+static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
static const unsigned headerLength = 2;
struct iovec newVec[nr + headerLength];
android_log_header_t header;
@@ -110,7 +108,7 @@
return -EINVAL;
}
- if (SNET_EVENT_LOG_TAG != get4LE(vec[0].iov_base)) {
+ if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
return -EPERM;
}
}
@@ -169,8 +167,7 @@
}
pmsgHeader.len += payloadSize;
- ret = TEMP_FAILURE_RETRY(
- writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
+ ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i));
if (ret < 0) {
ret = errno ? -errno : -ENOTCONN;
}
@@ -203,11 +200,8 @@
}
/* Write a buffer as filename references (tag = <basedir>:<basename>) */
-LIBLOG_ABI_PRIVATE ssize_t __android_log_pmsg_file_write(log_id_t logId,
- char prio,
- const char* filename,
- const char* buf,
- size_t len) {
+ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
+ const char* buf, size_t len) {
bool weOpened;
size_t length, packet_len;
const char* tag;
@@ -249,13 +243,11 @@
vec[1].iov_len = length;
weOpened = false;
- for (ts.tv_nsec = 0, length = len; length;
- ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
+ for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
ssize_t ret;
size_t transfer;
- if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
- ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+ if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
len -= length;
break;
}
diff --git a/liblog/properties.c b/liblog/properties.cpp
similarity index 83%
rename from liblog/properties.c
rename to liblog/properties.cpp
index 11be827..2e0a8c9 100644
--- a/liblog/properties.c
+++ b/liblog/properties.cpp
@@ -14,9 +14,10 @@
** limitations under the License.
*/
+#include <log/log_properties.h>
+
#include <ctype.h>
#include <pthread.h>
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
@@ -160,11 +161,11 @@
}
if (!last_tag || !last_tag[0]) {
if (!last_tag) {
- last_tag = calloc(1, len + 1);
+ last_tag = static_cast<char*>(calloc(1, len + 1));
last_tag_len = 0;
if (last_tag) last_tag_len = len + 1;
} else if (len >= last_tag_len) {
- last_tag = realloc(last_tag, len + 1);
+ last_tag = static_cast<char*>(realloc(last_tag, len + 1));
last_tag_len = 0;
if (last_tag) last_tag_len = len + 1;
}
@@ -258,26 +259,22 @@
case 'A': return ANDROID_LOG_FATAL;
case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */
case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
- /* clang-format on */
+ /* clang-format on */
}
return default_prio;
}
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable_len(int prio, const char* tag,
- size_t len,
- int default_prio) {
+int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) {
int logLevel = __android_log_level(tag, len, default_prio);
return logLevel >= 0 && prio >= logLevel;
}
-LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char* tag,
- int default_prio) {
- int logLevel =
- __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
+int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
+ int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
return logLevel >= 0 && prio >= logLevel;
}
-LIBLOG_ABI_PUBLIC int __android_log_is_debuggable() {
+int __android_log_is_debuggable() {
static uint32_t serial;
static struct cache_char tag_cache;
static const char key[] = "ro.debuggable";
@@ -286,7 +283,7 @@
if (tag_cache.c) { /* ro property does not change after set */
ret = tag_cache.c == '1';
} else if (lock()) {
- struct cache_char temp_cache = { { NULL, -1 }, '\0' };
+ struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
refresh_cache(&temp_cache, key);
ret = temp_cache.c == '1';
} else {
@@ -332,8 +329,7 @@
return self->evaluate(self);
}
- change_detected = check_cache(&self->cache_persist.cache) ||
- check_cache(&self->cache_ro.cache);
+ change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
current_serial = __system_property_area_serial();
if (current_serial != self->serial) {
change_detected = 1;
@@ -364,16 +360,13 @@
* Timestamp state generally remains constant, but can change at any time
* to handle developer requirements.
*/
-LIBLOG_ABI_PUBLIC clockid_t android_log_clockid() {
- static struct cache2_char clockid = {
- PTHREAD_MUTEX_INITIALIZER, 0,
- "persist.logd.timestamp", { { NULL, -1 }, '\0' },
- "ro.logd.timestamp", { { NULL, -1 }, '\0' },
- evaluate_persist_ro
- };
+clockid_t android_log_clockid() {
+ static struct cache2_char clockid = {PTHREAD_MUTEX_INITIALIZER, 0,
+ "persist.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
+ "ro.logd.timestamp", {{NULL, 0xFFFFFFFF}, '\0'},
+ evaluate_persist_ro};
- return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC
- : CLOCK_REALTIME;
+ return (tolower(do_cache2_char(&clockid)) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
}
/*
@@ -386,13 +379,12 @@
return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
}
-LIBLOG_ABI_PUBLIC int __android_log_security() {
+int __android_log_security() {
static struct cache2_char security = {
- PTHREAD_MUTEX_INITIALIZER, 0,
- "persist.logd.security", { { NULL, -1 }, BOOLEAN_FALSE },
- "ro.device_owner", { { NULL, -1 }, BOOLEAN_FALSE },
- evaluate_security
- };
+ PTHREAD_MUTEX_INITIALIZER, 0,
+ "persist.logd.security", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ "ro.device_owner", {{NULL, 0xFFFFFFFF}, BOOLEAN_FALSE},
+ evaluate_security};
return do_cache2_char(&security);
}
@@ -423,8 +415,7 @@
char property[PROP_VALUE_MAX];
};
-static void refresh_cache_property(struct cache_property* cache,
- const char* key) {
+static void refresh_cache_property(struct cache_property* cache, const char* key) {
if (!cache->cache.pinfo) {
cache->cache.pinfo = __system_property_find(key);
if (!cache->cache.pinfo) {
@@ -436,19 +427,18 @@
}
/* get boolean with the logger twist that supports eng adjustments */
-LIBLOG_ABI_PRIVATE bool __android_logger_property_get_bool(const char* key,
- int flag) {
- struct cache_property property = { { NULL, -1 }, { 0 } };
+bool __android_logger_property_get_bool(const char* key, int flag) {
+ struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
char newkey[strlen("persist.") + strlen(key) + 1];
snprintf(newkey, sizeof(newkey), "ro.%s", key);
refresh_cache_property(&property, newkey);
property.cache.pinfo = NULL;
- property.cache.serial = -1;
+ property.cache.serial = 0xFFFFFFFF;
snprintf(newkey, sizeof(newkey), "persist.%s", key);
refresh_cache_property(&property, newkey);
property.cache.pinfo = NULL;
- property.cache.serial = -1;
+ property.cache.serial = 0xFFFFFFFF;
}
refresh_cache_property(&property, key);
@@ -477,8 +467,7 @@
}
if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
- __android_logger_property_get_bool("ro.config.low_ram",
- BOOL_DEFAULT_FALSE)) {
+ __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
return false;
}
if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
@@ -488,7 +477,7 @@
return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
}
-LIBLOG_ABI_PRIVATE bool __android_logger_valid_buffer_size(unsigned long value) {
+bool __android_logger_valid_buffer_size(unsigned long value) {
static long pages, pagesize;
unsigned long maximum;
@@ -531,8 +520,7 @@
unsigned long (*const evaluate)(const struct cache2_property_size* self);
};
-static inline unsigned long do_cache2_property_size(
- struct cache2_property_size* self) {
+static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
uint32_t current_serial;
int change_detected;
unsigned long v;
@@ -542,8 +530,7 @@
return self->evaluate(self);
}
- change_detected = check_cache(&self->cache_persist.cache) ||
- check_cache(&self->cache_ro.cache);
+ change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
current_serial = __system_property_area_serial();
if (current_serial != self->serial) {
change_detected = 1;
@@ -560,8 +547,7 @@
return v;
}
-static unsigned long property_get_size_from_cache(
- const struct cache_property* cache) {
+static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
char* cp;
unsigned long value = strtoul(cache->property, &cp, 10);
@@ -569,11 +555,11 @@
case 'm':
case 'M':
value *= 1024;
- /* FALLTHRU */
+ [[fallthrough]];
case 'k':
case 'K':
value *= 1024;
- /* FALLTHRU */
+ [[fallthrough]];
case '\0':
break;
@@ -588,8 +574,7 @@
return value;
}
-static unsigned long evaluate_property_get_size(
- const struct cache2_property_size* self) {
+static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
unsigned long size = property_get_size_from_cache(&self->cache_persist);
if (size) {
return size;
@@ -597,41 +582,39 @@
return property_get_size_from_cache(&self->cache_ro);
}
-LIBLOG_ABI_PRIVATE unsigned long __android_logger_get_buffer_size(log_id_t logId) {
+unsigned long __android_logger_get_buffer_size(log_id_t logId) {
static const char global_tunable[] = "persist.logd.size"; /* Settings App */
static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
static struct cache2_property_size global = {
- /* clang-format off */
+ /* clang-format off */
PTHREAD_MUTEX_INITIALIZER, 0,
- global_tunable, { { NULL, -1 }, {} },
- global_default, { { NULL, -1 }, {} },
+ global_tunable, { { NULL, 0xFFFFFFFF }, {} },
+ global_default, { { NULL, 0xFFFFFFFF }, {} },
evaluate_property_get_size
- /* clang-format on */
+ /* clang-format on */
};
char key_persist[strlen(global_tunable) + strlen(".security") + 1];
char key_ro[strlen(global_default) + strlen(".security") + 1];
struct cache2_property_size local = {
- /* clang-format off */
+ /* clang-format off */
PTHREAD_MUTEX_INITIALIZER, 0,
- key_persist, { { NULL, -1 }, {} },
- key_ro, { { NULL, -1 }, {} },
+ key_persist, { { NULL, 0xFFFFFFFF }, {} },
+ key_ro, { { NULL, 0xFFFFFFFF }, {} },
evaluate_property_get_size
- /* clang-format on */
+ /* clang-format on */
};
unsigned long property_size, default_size;
default_size = do_cache2_property_size(&global);
if (!default_size) {
- default_size = __android_logger_property_get_bool("ro.config.low_ram",
- BOOL_DEFAULT_FALSE)
+ default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
? LOG_BUFFER_MIN_SIZE /* 64K */
: LOG_BUFFER_SIZE; /* 256K */
}
snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
android_log_id_to_name(logId));
- snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default,
- android_log_id_to_name(logId));
+ snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
property_size = do_cache2_property_size(&local);
if (!property_size) {
diff --git a/liblog/stderr_write.c b/liblog/stderr_write.cpp
similarity index 89%
rename from liblog/stderr_write.c
rename to liblog/stderr_write.cpp
index dbe5309..e324a7c 100644
--- a/liblog/stderr_write.c
+++ b/liblog/stderr_write.cpp
@@ -37,16 +37,15 @@
#include <log/event_tag_map.h>
#include <log/log.h>
#include <log/logprint.h>
-#include <log/uio.h>
#include "log_portability.h"
#include "logger.h"
+#include "uio.h"
static int stderrOpen();
static void stderrClose();
static int stderrAvailable(log_id_t logId);
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr);
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
struct stderrContext {
AndroidLogFormat* logformat;
@@ -55,14 +54,14 @@
#endif
};
-LIBLOG_HIDDEN struct android_log_transport_write stderrLoggerWrite = {
- .node = { &stderrLoggerWrite.node, &stderrLoggerWrite.node },
- .context.priv = NULL,
- .name = "stderr",
- .available = stderrAvailable,
- .open = stderrOpen,
- .close = stderrClose,
- .write = stderrWrite,
+struct android_log_transport_write stderrLoggerWrite = {
+ .node = {&stderrLoggerWrite.node, &stderrLoggerWrite.node},
+ .context.priv = NULL,
+ .name = "stderr",
+ .available = stderrAvailable,
+ .open = stderrOpen,
+ .close = stderrClose,
+ .write = stderrWrite,
};
static int stderrOpen() {
@@ -78,7 +77,7 @@
return fileno(stderr);
}
- ctx = calloc(1, sizeof(struct stderrContext));
+ ctx = static_cast<stderrContext*>(calloc(1, sizeof(stderrContext)));
if (!ctx) {
return -ENOMEM;
}
@@ -123,7 +122,7 @@
}
static void stderrClose() {
- struct stderrContext* ctx = stderrLoggerWrite.context.priv;
+ stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
if (ctx) {
stderrLoggerWrite.context.priv = NULL;
@@ -147,14 +146,13 @@
return 1;
}
-static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
- size_t nr) {
+static int stderrWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
struct log_msg log_msg;
AndroidLogEntry entry;
char binaryMsgBuf[1024];
int err;
size_t i;
- struct stderrContext* ctx = stderrLoggerWrite.context.priv;
+ stderrContext* ctx = static_cast<stderrContext*>(stderrLoggerWrite.context.priv);
if (!ctx) return -EBADF;
if (!vec || !nr) return -EINVAL;
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
new file mode 100644
index 0000000..d9d1a21
--- /dev/null
+++ b/liblog/tests/Android.bp
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2013-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// -----------------------------------------------------------------------------
+// Benchmarks.
+// -----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell liblog-benchmarks
+cc_benchmark {
+ name: "liblog-benchmarks",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ shared_libs: [
+ "libm",
+ "libbase",
+ "libcutils",
+ ],
+ static_libs: ["liblog"],
+ srcs: ["liblog_benchmark.cpp"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "liblog-tests-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+ srcs: [
+ "libc_test.cpp",
+ "liblog_test_default.cpp",
+ "liblog_test_stderr.cpp",
+ "log_id_test.cpp",
+ "log_radio_test.cpp",
+ "log_read_test.cpp",
+ "log_system_test.cpp",
+ "log_time_test.cpp",
+ "log_wrap_test.cpp",
+ "logprint_test.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ ],
+ static_libs: ["liblog"],
+}
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
+cc_test {
+ name: "liblog-unit-tests",
+ defaults: ["liblog-tests-defaults"],
+}
+
+cc_test {
+ name: "CtsLiblogTestCases",
+ defaults: ["liblog-tests-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ cflags: ["-DNO_PSTORE"],
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
deleted file mode 100644
index cfa849b..0000000
--- a/liblog/tests/Android.mk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# Copyright (C) 2013-2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks.
-# -----------------------------------------------------------------------------
-
-test_module_prefix := liblog-
-test_tags := tests
-
-benchmark_c_flags := \
- -Wall \
- -Wextra \
- -Werror \
- -fno-builtin \
-
-benchmark_src_files := \
- liblog_benchmark.cpp
-
-# Build benchmarks for the device. Run with:
-# adb shell liblog-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(benchmark_c_flags)
-LOCAL_SHARED_LIBRARIES += liblog libm libbase
-LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-cts_src_files := \
- libc_test.cpp \
- liblog_test_default.cpp \
- liblog_test_local.cpp \
- liblog_test_stderr.cpp \
- liblog_test_stderr_local.cpp \
- log_id_test.cpp \
- log_radio_test.cpp \
- log_read_test.cpp \
- log_system_test.cpp \
- log_time_test.cpp \
- log_wrap_test.cpp
-
-test_src_files := \
- $(cts_src_files) \
-
-# Build tests for the device (with .so). Run with:
-# adb shell /data/nativetest/liblog-unit-tests/liblog-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLiblogTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags) -DNO_PSTORE
-LOCAL_SRC_FILES := $(cts_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.liblog
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_LDLIBS_linux := -lrt
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/liblog/tests/AndroidTest.xml b/liblog/tests/AndroidTest.xml
index 7b64433..c167478 100644
--- a/liblog/tests/AndroidTest.xml
+++ b/liblog/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Logging Library test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsLiblogTestCases->/data/local/tmp/CtsLiblogTestCases" />
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 6d78ed6..3534eb8 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -23,7 +23,7 @@
#ifdef __ANDROID__
#ifndef NO_PSTORE
FILE* fp;
- ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
+ ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "ae")));
static const char message[] = "libc.__pstore_append\n";
ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
int fflushReturn = fflush(fp);
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c2f3f83..21d12a1 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -21,6 +21,7 @@
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <unistd.h>
#include <unordered_set>
@@ -55,8 +56,8 @@
*/
static void BM_log_maximum_retry(benchmark::State& state) {
while (state.KeepRunning()) {
- LOG_FAILURE_RETRY(__android_log_print(
- ANDROID_LOG_INFO, "BM_log_maximum_retry", "%zu", state.iterations()));
+ LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%" PRIu64,
+ state.iterations()));
}
}
BENCHMARK(BM_log_maximum_retry);
@@ -68,8 +69,7 @@
*/
static void BM_log_maximum(benchmark::State& state) {
while (state.KeepRunning()) {
- __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
- state.iterations());
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%" PRIu64, state.iterations());
}
}
BENCHMARK(BM_log_maximum);
@@ -171,7 +171,7 @@
* Measure the time it takes to submit the android logging data to pstore
*/
static void BM_pmsg_short(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -247,7 +247,7 @@
* best case aligned single block.
*/
static void BM_pmsg_short_aligned(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -285,7 +285,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -322,7 +322,7 @@
* best case aligned single block.
*/
static void BM_pmsg_short_unaligned1(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -360,7 +360,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -397,7 +397,7 @@
* best case aligned single block.
*/
static void BM_pmsg_long_aligned(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -435,7 +435,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -470,7 +470,7 @@
* best case aligned single block.
*/
static void BM_pmsg_long_unaligned1(benchmark::State& state) {
- int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+ int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
if (pstore_fd < 0) {
state.SkipWithError("/dev/pmsg0");
return;
@@ -508,7 +508,7 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ fprintf(stderr, "&buffer=0x%p iterations=%" PRIu64 "\n", &buffer->pmsg_header,
state.iterations());
}
@@ -559,7 +559,7 @@
/* performance test */
static void BM_sprintf_overhead(benchmark::State& state) {
while (state.KeepRunning()) {
- test_print("BM_sprintf_overhead:%zu", state.iterations());
+ test_print("BM_sprintf_overhead:%" PRIu64, state.iterations());
state.PauseTiming();
logd_yield();
state.ResumeTiming();
@@ -574,8 +574,7 @@
*/
static void BM_log_print_overhead(benchmark::State& state) {
while (state.KeepRunning()) {
- __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
- state.iterations());
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%" PRIu64, state.iterations());
state.PauseTiming();
logd_yield();
state.ResumeTiming();
@@ -948,8 +947,8 @@
// Must be functionally identical to liblog internal __send_log_msg.
static void send_to_control(char* buf, size_t len) {
- int sock = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM);
+ int sock =
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
if (sock < 0) return;
size_t writeLen = strlen(buf) + 1;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 383d0e7..1f87b3e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -52,22 +52,6 @@
#endif
#endif
-#if (!defined(USING_LOGGER_DEFAULT) || !defined(USING_LOGGER_LOCAL) || \
- !defined(USING_LOGGER_STDERR))
-#ifdef liblog // a binary clue that we are overriding the test names
-// Does not support log reading blocking feature yet
-// Does not support LOG_ID_SECURITY (unless we set LOGGER_LOCAL | LOGGER_LOGD)
-// Assume some common aspects are tested by USING_LOGGER_DEFAULT:
-// Does not need to _retest_ pmsg functionality
-// Does not need to _retest_ property handling as it is a higher function
-// Does not need to _retest_ event mapping functionality
-// Does not need to _retest_ ratelimit
-// Does not need to _retest_ logprint
-#define USING_LOGGER_LOCAL
-#else
-#define USING_LOGGER_DEFAULT
-#endif
-#endif
#ifdef USING_LOGGER_STDERR
#define SUPPORTS_END_TO_END 0
#else
@@ -109,7 +93,7 @@
static std::string popenToString(const std::string& command) {
std::string ret;
- FILE* fp = popen(command.c_str(), "r");
+ FILE* fp = popen(command.c_str(), "re");
if (fp) {
if (!android::base::ReadFdToString(fileno(fp), &ret)) ret = "";
pclose(fp);
@@ -175,7 +159,7 @@
#endif
TEST(liblog, __android_log_btwrite__android_logger_list_read) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
#ifdef TEST_PREFIX
TEST_PREFIX
#endif
@@ -269,7 +253,7 @@
#endif
}
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
static void print_transport(const char* prefix, int logger) {
static const char orstr[] = " | ";
@@ -297,16 +281,11 @@
fprintf(stderr, "%sLOGGER_NULL", prefix);
prefix = orstr;
}
- if (logger & LOGGER_LOCAL) {
- fprintf(stderr, "%sLOGGER_LOCAL", prefix);
- prefix = orstr;
- }
if (logger & LOGGER_STDERR) {
fprintf(stderr, "%sLOGGER_STDERR", prefix);
prefix = orstr;
}
- logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_LOCAL |
- LOGGER_STDERR);
+ logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_STDERR);
if (logger) {
fprintf(stderr, "%s0x%x", prefix, logger);
prefix = orstr;
@@ -321,7 +300,7 @@
// and behind us, to make us whole. We could incorporate a prefix and
// suffix test to make this standalone, but opted to not complicate this.
TEST(liblog, android_set_log_transport) {
-#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef __ANDROID__
#ifdef TEST_PREFIX
TEST_PREFIX
#endif
@@ -632,7 +611,7 @@
buf_write_test("\n Hello World \n");
}
-#ifndef USING_LOGGER_LOCAL // requires blocking reader functionality
+#ifdef USING_LOGGER_DEFAULT // requires blocking reader functionality
#ifdef TEST_PREFIX
static unsigned signaled;
static log_time signal_time;
@@ -666,7 +645,7 @@
char buffer[512];
snprintf(buffer, sizeof(buffer), "/proc/%u/stat", pid);
- FILE* fp = fopen(buffer, "r");
+ FILE* fp = fopen(buffer, "re");
if (!fp) {
return;
}
@@ -944,7 +923,7 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#endif // !USING_LOGGER_LOCAL
+#endif // USING_LOGGER_DEFAULT
#ifdef TEST_PREFIX
static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
@@ -2417,7 +2396,7 @@
}
// Do not retest logger list handling
-#if (defined(TEST_PREFIX) || !defined(USING_LOGGER_LOCAL))
+#ifdef TEST_PREFIX
static int is_real_element(int type) {
return ((type == EVENT_TYPE_INT) || (type == EVENT_TYPE_LONG) ||
(type == EVENT_TYPE_STRING) || (type == EVENT_TYPE_FLOAT));
@@ -2572,7 +2551,7 @@
return 0;
}
-#endif // TEST_PREFIX || !USING_LOGGER_LOCAL
+#endif // TEST_PREFIX
#ifdef TEST_PREFIX
static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
@@ -3181,41 +3160,6 @@
}
#endif // USING_LOGGER_DEFAULT
-#ifdef USING_LOGGER_DEFAULT // Do not retest ratelimit
-TEST(liblog, __android_log_ratelimit) {
- time_t state = 0;
-
- errno = 42;
- // Prime
- __android_log_ratelimit(3, &state);
- EXPECT_EQ(errno, 42);
- // Check
- EXPECT_FALSE(__android_log_ratelimit(3, &state));
- sleep(1);
- EXPECT_FALSE(__android_log_ratelimit(3, &state));
- sleep(4);
- EXPECT_TRUE(__android_log_ratelimit(3, &state));
- sleep(5);
- EXPECT_TRUE(__android_log_ratelimit(3, &state));
-
- // API checks
- IF_ALOG_RATELIMIT_LOCAL(3, &state) {
- EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
- }
-
- IF_ALOG_RATELIMIT() {
- ;
- }
- else {
- EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
- }
- IF_ALOG_RATELIMIT() {
- EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
- }
- // Do not test default seconds, to allow liblog to tune freely
-}
-#endif // USING_LOGGER_DEFAULT
-
#ifdef USING_LOGGER_DEFAULT // Do not retest event mapping functionality
TEST(liblog, android_lookupEventTagNum) {
#ifdef __ANDROID__
diff --git a/liblog/tests/liblog_test_local.cpp b/liblog/tests/liblog_test_local.cpp
deleted file mode 100644
index 451beca..0000000
--- a/liblog/tests/liblog_test_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_local
-#define TEST_LOGGER LOGGER_LOCAL
-#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_stderr_local.cpp b/liblog/tests/liblog_test_stderr_local.cpp
deleted file mode 100644
index bb5c7ae..0000000
--- a/liblog/tests/liblog_test_stderr_local.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include <log/log_transport.h>
-#define liblog liblog_stderr_local
-#define TEST_LOGGER (LOGGER_LOCAL | LOGGER_STDERR)
-#include "liblog_test.cpp"
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index f202c67..fa1255e 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -91,7 +91,7 @@
"logcat -b radio --pid=%u -d -s"
" TEST__RLOGV TEST__RLOGD TEST__RLOGI TEST__RLOGW TEST__RLOGE",
(unsigned)getpid());
- FILE* fp = popen(buf.c_str(), "r");
+ FILE* fp = popen(buf.c_str(), "re");
int count = 0;
int count_false = 0;
if (fp) {
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index 0656c0b..13f026d 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -91,7 +91,7 @@
"logcat -b system --pid=%u -d -s"
" TEST__SLOGV TEST__SLOGD TEST__SLOGI TEST__SLOGW TEST__SLOGE",
(unsigned)getpid());
- FILE* fp = popen(buf.c_str(), "r");
+ FILE* fp = popen(buf.c_str(), "re");
int count = 0;
int count_false = 0;
if (fp) {
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
index 0ae1d18..47fe594 100644
--- a/liblog/tests/log_time_test.cpp
+++ b/liblog/tests/log_time_test.cpp
@@ -21,12 +21,6 @@
#include <log/log_time.h>
TEST(liblog, log_time) {
-#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
- log_time(CLOCK_MONOTONIC);
-
- EXPECT_EQ(log_time, log_time::EPOCH);
-#endif
-
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
log_time tl(ts);
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
new file mode 100644
index 0000000..7ca02ac
--- /dev/null
+++ b/liblog/tests/logprint_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+size_t convertPrintable(char* p, const char* message, size_t messageLen);
+
+TEST(liblog, convertPrintable_ascii) {
+ auto input = "easy string, output same";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_escapes) {
+ // Note that \t is not escaped.
+ auto input = "escape\a\b\t\v\f\r\\";
+ auto expected_output = "escape\\a\\b\t\\v\\f\\r\\\\";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_validutf8) {
+ auto input = u8"¢ह€𐍈";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(input));
+ EXPECT_STREQ(input, output);
+}
+
+TEST(liblog, convertPrintable_invalidutf8) {
+ auto input = "\x80\xC2\x01\xE0\xA4\x06\xE0\x06\xF0\x90\x8D\x06\xF0\x90\x06\xF0\x0E";
+ auto expected_output =
+ "\\x80\\xC2\\x01\\xE0\\xA4\\x06\\xE0\\x06\\xF0\\x90\\x8D\\x06\\xF0\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
+
+TEST(liblog, convertPrintable_mixed) {
+ auto input =
+ u8"\x80\xC2¢ह€𐍈\x01\xE0\xA4\x06¢ह€𐍈\xE0\x06\a\b\xF0\x90¢ह€𐍈\x8D\x06\xF0\t\t\x90\x06\xF0\x0E";
+ auto expected_output =
+ u8"\\x80\\xC2¢ह€𐍈\\x01\\xE0\\xA4\\x06¢ह€𐍈\\xE0\\x06\\a\\b\\xF0\\x90¢ह€𐍈\\x8D\\x06\\xF0\t\t"
+ u8"\\x90\\x06\\xF0\\x0E";
+ auto output_size = convertPrintable(nullptr, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+
+ char output[output_size];
+
+ output_size = convertPrintable(output, input, strlen(input));
+ EXPECT_EQ(output_size, strlen(expected_output));
+ EXPECT_STREQ(expected_output, output);
+}
diff --git a/liblog/uio.c b/liblog/uio.c
deleted file mode 100644
index e127202..0000000
--- a/liblog/uio.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(_WIN32)
-
-#include <unistd.h>
-
-#include <log/uio.h>
-
-#include "log_portability.h"
-
-LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec* vecs, int count) {
- int total = 0;
-
- for (; count > 0; count--, vecs++) {
- char* buf = vecs->iov_base;
- int len = vecs->iov_len;
-
- while (len > 0) {
- int ret = read(fd, buf, len);
- if (ret < 0) {
- if (total == 0) total = -1;
- goto Exit;
- }
- if (ret == 0) goto Exit;
-
- total += ret;
- buf += ret;
- len -= ret;
- }
- }
-Exit:
- return total;
-}
-
-LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec* vecs, int count) {
- int total = 0;
-
- for (; count > 0; count--, vecs++) {
- const char* buf = vecs->iov_base;
- int len = vecs->iov_len;
-
- while (len > 0) {
- int ret = write(fd, buf, len);
- if (ret < 0) {
- if (total == 0) total = -1;
- goto Exit;
- }
- if (ret == 0) goto Exit;
-
- total += ret;
- buf += ret;
- len -= ret;
- }
- }
-Exit:
- return total;
-}
-
-#endif
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/liblog/uio.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to liblog/uio.h
index 410d379..c85893c 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/liblog/uio.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#if defined(_WIN32)
+#include <stddef.h>
+struct iovec {
+ void* iov_base;
+ size_t iov_len;
+};
+#else
+#include <sys/uio.h>
+#endif
diff --git a/libmeminfo/.clang-format b/libmeminfo/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmeminfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
new file mode 100644
index 0000000..8dcc77b
--- /dev/null
+++ b/libmeminfo/Android.bp
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "libmeminfo_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprocinfo",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+
+ },
+}
+
+cc_library {
+ name: "libmeminfo",
+ host_supported: true,
+ defaults: ["libmeminfo_defaults"],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: ["libbase"],
+ srcs: [
+ "pageacct.cpp",
+ "procmeminfo.cpp",
+ "sysmeminfo.cpp",
+ ],
+}
+
+cc_test {
+ name: "libmeminfo_test",
+ defaults: ["libmeminfo_defaults"],
+
+ static_libs: [
+ "libmeminfo",
+ "libbase",
+ "liblog",
+ ],
+
+ srcs: [
+ "libmeminfo_test.cpp"
+ ],
+
+ data: [
+ "testdata1/*",
+ "testdata2/*"
+ ],
+}
+
+cc_benchmark {
+ name: "libmeminfo_benchmark",
+ srcs: [
+ "libmeminfo_benchmark.cpp",
+ ],
+ static_libs : [
+ "libbase",
+ "liblog",
+ "libmeminfo",
+ "libprocinfo",
+ ],
+
+ data: [
+ "testdata1/*",
+ ],
+}
diff --git a/libmeminfo/OWNERS b/libmeminfo/OWNERS
new file mode 100644
index 0000000..26e71fe
--- /dev/null
+++ b/libmeminfo/OWNERS
@@ -0,0 +1 @@
+sspatil@google.com
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
new file mode 100644
index 0000000..2fc7867
--- /dev/null
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+struct MemUsage {
+ uint64_t vss;
+ uint64_t rss;
+ uint64_t pss;
+ uint64_t uss;
+
+ uint64_t swap;
+ uint64_t swap_pss;
+
+ uint64_t private_clean;
+ uint64_t private_dirty;
+ uint64_t shared_clean;
+ uint64_t shared_dirty;
+
+ MemUsage()
+ : vss(0),
+ rss(0),
+ pss(0),
+ uss(0),
+ swap(0),
+ swap_pss(0),
+ private_clean(0),
+ private_dirty(0),
+ shared_clean(0),
+ shared_dirty(0) {}
+
+ ~MemUsage() = default;
+
+ void clear() {
+ vss = rss = pss = uss = swap = swap_pss = 0;
+ private_clean = private_dirty = shared_clean = shared_dirty = 0;
+ }
+};
+
+struct Vma {
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint16_t flags;
+ std::string name;
+
+ Vma() : start(0), end(0), offset(0), flags(0), name("") {}
+ Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
+ : start(s), end(e), offset(off), flags(f), name(n) {}
+ ~Vma() = default;
+
+ void clear() { memset(&usage, 0, sizeof(usage)); }
+
+ // Memory usage of this mapping.
+ MemUsage usage;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
new file mode 100644
index 0000000..8483d84
--- /dev/null
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace meminfo {
+
+class PageAcct final {
+ // Class for per-page accounting by using kernel provided interfaces like
+ // kpagecount, kpageflags etc.
+ public:
+ static bool KernelHasPageIdle() {
+ return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
+ }
+
+ bool InitPageAcct(bool pageidle_enable = false);
+ bool PageFlags(uint64_t pfn, uint64_t* flags);
+ bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
+
+ int IsPageIdle(uint64_t pfn);
+
+ // The only way to create PageAcct object
+ static PageAcct& Instance() {
+ static PageAcct instance;
+ return instance;
+ }
+
+ ~PageAcct() = default;
+
+ private:
+ PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
+ int MarkPageIdle(uint64_t pfn) const;
+ int GetPageIdle(uint64_t pfn) const;
+
+ // Non-copyable & Non-movable
+ PageAcct(const PageAcct&) = delete;
+ PageAcct& operator=(const PageAcct&) = delete;
+ PageAcct& operator=(PageAcct&&) = delete;
+ PageAcct(PageAcct&&) = delete;
+
+ ::android::base::unique_fd kpagecount_fd_;
+ ::android::base::unique_fd kpageflags_fd_;
+ ::android::base::unique_fd pageidle_fd_;
+};
+
+// Returns if the page present bit is set in the value
+// passed in.
+bool page_present(uint64_t pagemap_val);
+
+// Returns if the page swapped bit is set in the value
+// passed in.
+bool page_swapped(uint64_t pagemap_val);
+
+// Returns the page frame number (physical page) from
+// pagemap value
+uint64_t page_pfn(uint64_t pagemap_val);
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
new file mode 100644
index 0000000..f782ec5
--- /dev/null
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "meminfo.h"
+
+namespace android {
+namespace meminfo {
+
+using VmaCallback = std::function<void(const Vma&)>;
+
+class ProcMemInfo final {
+ // Per-process memory accounting
+ public:
+ // Reset the working set accounting of the process via /proc/<pid>/clear_refs
+ static bool ResetWorkingSet(pid_t pid);
+
+ ProcMemInfo(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0);
+
+ const std::vector<Vma>& Maps();
+ const MemUsage& Usage();
+ const MemUsage& Wss();
+
+ // Same as Maps() except, only valid for reading working set using CONFIG_IDLE_PAGE_TRACKING
+ // support in kernel. If the kernel support doesn't exist, the function will return an empty
+ // vector.
+ const std::vector<Vma>& MapsWithPageIdle();
+
+ // Same as Maps() except, do not read the usage stats for each map.
+ const std::vector<Vma>& MapsWithoutUsageStats();
+
+ // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
+ // constant reference to the vma vector after the collection is done.
+ //
+ // Each 'struct Vma' is *fully* populated by this method (unlike SmapsOrRollup).
+ const std::vector<Vma>& Smaps(const std::string& path = "");
+
+ // This method reads /proc/<pid>/smaps and calls the callback() for each
+ // vma or map that it finds. The map is converted to 'struct Vma' object which is then
+ // passed to the callback.
+ // Returns 'false' if the file is malformed.
+ bool ForEachVma(const VmaCallback& callback);
+
+ // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
+ // Pss and Private memory usage in 'stats'. In particular, the method only populates the fields
+ // of the MemUsage structure that are intended to be used by Android's periodic Pss collection.
+ //
+ // The method populates the following statistics in order to be fast an efficient.
+ // Pss
+ // Rss
+ // Uss
+ // private_clean
+ // private_dirty
+ // SwapPss
+ // All other fields of MemUsage are zeroed.
+ bool SmapsOrRollup(MemUsage* stats) const;
+
+ // Used to parse either of /proc/<pid>/{smaps, smaps_rollup} and record the process's
+ // Pss.
+ // Returns 'true' on success and the value of Pss in the out parameter.
+ bool SmapsOrRollupPss(uint64_t* pss) const;
+
+ const std::vector<uint16_t>& SwapOffsets();
+
+ // Reads /proc/<pid>/pagemap for this process for each page within
+ // the 'vma' and stores that in 'pagemap'. It is assumed that the 'vma'
+ // is obtained by calling Maps() or 'ForEachVma' for the same object. No special checks
+ // are made to see if 'vma' is *valid*.
+ // Returns false if anything goes wrong, 'true' otherwise.
+ bool PageMap(const Vma& vma, std::vector<uint64_t>* pagemap);
+
+ ~ProcMemInfo() = default;
+
+ private:
+ bool ReadMaps(bool get_wss, bool use_pageidle = false, bool get_usage_stats = true);
+ bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);
+
+ pid_t pid_;
+ bool get_wss_;
+ uint64_t pgflags_;
+ uint64_t pgflags_mask_;
+
+ std::vector<Vma> maps_;
+
+ MemUsage usage_;
+ std::vector<uint16_t> swap_offsets_;
+};
+
+// Makes callback for each 'vma' or 'map' found in file provided. The file is expected to be in the
+// same format as /proc/<pid>/smaps. Returns 'false' if the file is malformed.
+bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback);
+
+// Returns if the kernel supports /proc/<pid>/smaps_rollup. Assumes that the
+// calling process has access to the /proc/<pid>/smaps_rollup.
+// Returns 'false' if the calling process has no permission to read the file if it exists
+// of if the file doesn't exist.
+bool IsSmapsRollupSupported(pid_t pid);
+
+// Same as ProcMemInfo::SmapsOrRollup but reads the statistics directly
+// from a file. The file MUST be in the same format as /proc/<pid>/smaps
+// or /proc/<pid>/smaps_rollup
+bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats);
+
+// Same as ProcMemInfo::SmapsOrRollupPss but reads the statistics directly
+// from a file and returns total Pss in kB. The file MUST be in the same format
+// as /proc/<pid>/smaps or /proc/<pid>/smaps_rollup
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss);
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
new file mode 100644
index 0000000..8388920
--- /dev/null
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+class SysMemInfo final {
+ // System or Global memory accounting
+ public:
+ static constexpr const char* kMemTotal = "MemTotal:";
+ static constexpr const char* kMemFree = "MemFree:";
+ static constexpr const char* kMemBuffers = "Buffers:";
+ static constexpr const char* kMemCached = "Cached:";
+ static constexpr const char* kMemShmem = "Shmem:";
+ static constexpr const char* kMemSlab = "Slab:";
+ static constexpr const char* kMemSReclaim = "SReclaimable:";
+ static constexpr const char* kMemSUnreclaim = "SUnreclaim:";
+ static constexpr const char* kMemSwapTotal = "SwapTotal:";
+ static constexpr const char* kMemSwapFree = "SwapFree:";
+ static constexpr const char* kMemMapped = "Mapped:";
+ static constexpr const char* kMemVmallocUsed = "VmallocUsed:";
+ static constexpr const char* kMemPageTables = "PageTables:";
+ static constexpr const char* kMemKernelStack = "KernelStack:";
+
+ static const std::vector<std::string> kDefaultSysMemInfoTags;
+
+ SysMemInfo() = default;
+
+ // Parse /proc/meminfo and read values that are needed
+ bool ReadMemInfo(const std::string& path = "/proc/meminfo");
+ bool ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+ const std::string& path = "/proc/meminfo");
+ bool ReadMemInfo(std::vector<uint64_t>* out, const std::string& path = "/proc/meminfo");
+
+ // Parse /proc/vmallocinfo and return total physical memory mapped
+ // in vmalloc area by the kernel.
+ // Note that this deliberately ignores binder buffers. They are _always_
+ // mapped in a process and are counted for in each process.
+ uint64_t ReadVmallocInfo();
+
+ // getters
+ uint64_t mem_total_kb() { return mem_in_kb_[kMemTotal]; }
+ uint64_t mem_free_kb() { return mem_in_kb_[kMemFree]; }
+ uint64_t mem_buffers_kb() { return mem_in_kb_[kMemBuffers]; }
+ uint64_t mem_cached_kb() { return mem_in_kb_[kMemCached]; }
+ uint64_t mem_shmem_kb() { return mem_in_kb_[kMemShmem]; }
+ uint64_t mem_slab_kb() { return mem_in_kb_[kMemSlab]; }
+ uint64_t mem_slab_reclaimable_kb() { return mem_in_kb_[kMemSReclaim]; }
+ uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_[kMemSUnreclaim]; }
+ uint64_t mem_swap_kb() { return mem_in_kb_[kMemSwapTotal]; }
+ uint64_t mem_swap_free_kb() { return mem_in_kb_[kMemSwapFree]; }
+ uint64_t mem_mapped_kb() { return mem_in_kb_[kMemMapped]; }
+ uint64_t mem_vmalloc_used_kb() { return mem_in_kb_[kMemVmallocUsed]; }
+ uint64_t mem_page_tables_kb() { return mem_in_kb_[kMemPageTables]; }
+ uint64_t mem_kernel_stack_kb() { return mem_in_kb_[kMemKernelStack]; }
+ uint64_t mem_zram_kb(const std::string& zram_dev = "");
+
+ private:
+ std::map<std::string, uint64_t> mem_in_kb_;
+ bool MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev);
+ bool ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+ std::function<void(const std::string&, uint64_t)> store_val);
+};
+
+// Parse /proc/vmallocinfo and return total physical memory mapped
+// in vmalloc area by the kernel. Note that this deliberately ignores binder buffers. They are
+// _always_ mapped in a process and are counted for in each process.
+uint64_t ReadVmallocInfo(const std::string& path = "/proc/vmallocinfo");
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/libdmabufinfo/Android.bp b/libmeminfo/libdmabufinfo/Android.bp
new file mode 100644
index 0000000..4aed45c
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/Android.bp
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "dmabufinfo_defaults",
+ static_libs: [
+ "libbase",
+ "libprocinfo",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
+cc_library_static {
+ name: "libdmabufinfo",
+ vendor_available: true,
+ defaults: ["dmabufinfo_defaults"],
+ export_include_dirs: ["include"],
+ srcs: [
+ "dmabufinfo.cpp",
+ ],
+}
+
+cc_test {
+ name: "dmabufinfo_test",
+ defaults: ["dmabufinfo_defaults"],
+ srcs: [
+ "dmabufinfo_test.cpp"
+ ],
+
+ static_libs: [
+ "libc++fs",
+ "libdmabufinfo",
+ "libion",
+ "libmeminfo",
+ ],
+}
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo.cpp b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
new file mode 100644
index 0000000..9fb22a1
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/dmabufinfo.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <procinfo/process_map.h>
+
+#include <dmabufinfo/dmabufinfo.h>
+
+namespace android {
+namespace dmabufinfo {
+
+static bool FileIsDmaBuf(const std::string& path) {
+ return ::android::base::StartsWith(path, "/dmabuf");
+}
+
+static bool ReadDmaBufFdInfo(pid_t pid, int fd, std::string* name, std::string* exporter,
+ uint64_t* count) {
+ std::string fdinfo = ::android::base::StringPrintf("/proc/%d/fdinfo/%d", pid, fd);
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fdinfo.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ LOG(ERROR) << "Failed to open dmabuf info from debugfs";
+ return false;
+ }
+
+ char* line = nullptr;
+ size_t len = 0;
+ while (getline(&line, &len, fp.get()) > 0) {
+ switch (line[0]) {
+ case 'c':
+ if (strncmp(line, "count:", 6) == 0) {
+ char* c = line + 6;
+ *count = strtoull(c, nullptr, 10);
+ }
+ break;
+ case 'e':
+ if (strncmp(line, "exp_name:", 9) == 0) {
+ char* c = line + 9;
+ *exporter = ::android::base::Trim(c);
+ }
+ break;
+ case 'n':
+ if (strncmp(line, "name:", 5) == 0) {
+ char* c = line + 5;
+ *name = ::android::base::Trim(std::string(c));
+ }
+ break;
+ }
+ }
+
+ free(line);
+ return true;
+}
+
+// TODO: std::filesystem::is_symlink fails to link on vendor code,
+// forcing this workaround.
+// Move back to libc++fs once it is vendor-available. See b/124012728
+static bool is_symlink(const char *filename)
+{
+ struct stat p_statbuf;
+ if (lstat(filename, &p_statbuf) < 0) {
+ return false;
+ }
+ if (S_ISLNK(p_statbuf.st_mode) == 1) {
+ return true;
+ }
+ return false;
+}
+
+static bool ReadDmaBufFdRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+ std::string fdpath = ::android::base::StringPrintf("/proc/%d/fd", pid);
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fdpath.c_str()), closedir);
+ if (!dir) {
+ LOG(ERROR) << "Failed to open " << fdpath << " directory" << std::endl;
+ return false;
+ }
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ std::string path =
+ ::android::base::StringPrintf("%s/%s", fdpath.c_str(), dent->d_name);
+
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") ||
+ !is_symlink(path.c_str())) {
+ continue;
+ }
+
+ std::string target;
+ if (!::android::base::Readlink(path, &target)) {
+ LOG(ERROR) << "Failed to find target for symlink: " << path;
+ return false;
+ }
+
+ if (!FileIsDmaBuf(target)) {
+ continue;
+ }
+
+ int fd;
+ if (!::android::base::ParseInt(dent->d_name, &fd)) {
+ LOG(ERROR) << "Dmabuf fd: " << path << " is invalid";
+ return false;
+ }
+
+ // Set defaults in case the kernel doesn't give us the information
+ // we need in fdinfo
+ std::string name = "<unknown>";
+ std::string exporter = "<unknown>";
+ uint64_t count = 0;
+ if (!ReadDmaBufFdInfo(pid, fd, &name, &exporter, &count)) {
+ LOG(ERROR) << "Failed to read fdinfo for: " << path;
+ return false;
+ }
+
+ struct stat sb;
+ if (stat(path.c_str(), &sb) < 0) {
+ PLOG(ERROR) << "Failed to stat: " << path;
+ return false;
+ }
+
+ uint64_t inode = sb.st_ino;
+ auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
+ [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
+ if (buf != dmabufs->end()) {
+ if (buf->name() == "" || buf->name() == "<unknown>") buf->SetName(name);
+ if (buf->exporter() == "" || buf->exporter() == "<unknown>") buf->SetExporter(exporter);
+ if (buf->count() == 0) buf->SetCount(count);
+ buf->AddFdRef(pid);
+ continue;
+ }
+
+ DmaBuffer& db = dmabufs->emplace_back(sb.st_ino, sb.st_blocks * 512, count, exporter, name);
+ db.AddFdRef(pid);
+ }
+
+ return true;
+}
+
+static bool ReadDmaBufMapRefs(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+ std::string mapspath = ::android::base::StringPrintf("/proc/%d/maps", pid);
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mapspath.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ LOG(ERROR) << "Failed to open maps for pid: " << pid;
+ return false;
+ }
+
+ char* line = nullptr;
+ size_t len = 0;
+
+ // Process the map if it is dmabuf. Add map reference to existing object in 'dmabufs'
+ // if it was already found. If it wasn't create a new one and append it to 'dmabufs'
+ auto account_dmabuf = [&](uint64_t start, uint64_t end, uint16_t /* flags */,
+ uint64_t /* pgoff */, ino_t inode, const char* name) {
+ // no need to look into this mapping if it is not dmabuf
+ if (!FileIsDmaBuf(std::string(name))) {
+ return;
+ }
+
+ auto buf = std::find_if(dmabufs->begin(), dmabufs->end(),
+ [&inode](const DmaBuffer& dbuf) { return dbuf.inode() == inode; });
+ if (buf != dmabufs->end()) {
+ buf->AddMapRef(pid);
+ return;
+ }
+
+ // We have a new buffer, but unknown count and name
+ DmaBuffer& dbuf = dmabufs->emplace_back(inode, end - start, 0, "<unknown>", "<unknown>");
+ dbuf.AddMapRef(pid);
+ };
+
+ while (getline(&line, &len, fp.get()) > 0) {
+ if (!::android::procinfo::ReadMapFileContent(line, account_dmabuf)) {
+ LOG(ERROR) << "Failed t parse maps for pid: " << pid;
+ return false;
+ }
+ }
+
+ free(line);
+ return true;
+}
+
+// Public methods
+bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs, const std::string& path) {
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ LOG(ERROR) << "Failed to open dmabuf info from debugfs";
+ return false;
+ }
+
+ char* line = nullptr;
+ size_t len = 0;
+ dmabufs->clear();
+ while (getline(&line, &len, fp.get()) > 0) {
+ // The new dmabuf bufinfo format adds inode number and a name at the end
+ // We are looking for lines as follows:
+ // size flags mode count exp_name ino name
+ // 01048576 00000002 00000007 00000001 ion 00018758 CAMERA
+ // 01048576 00000002 00000007 00000001 ion 00018758
+ uint64_t size, count;
+ char* exporter_name = nullptr;
+ ino_t inode;
+ char* name = nullptr;
+ int matched = sscanf(line, "%" SCNu64 "%*x %*x %" SCNu64 " %ms %lu %ms", &size, &count,
+ &exporter_name, &inode, &name);
+ if (matched < 4) {
+ continue;
+ }
+ dmabufs->emplace_back(inode, size, count, exporter_name, matched > 4 ? name : "");
+ free(exporter_name);
+ free(name);
+ }
+
+ free(line);
+
+ return true;
+}
+
+bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+ dmabufs->clear();
+ return AppendDmaBufInfo(pid, dmabufs);
+}
+
+bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs) {
+ if (!ReadDmaBufFdRefs(pid, dmabufs)) {
+ LOG(ERROR) << "Failed to read dmabuf fd references";
+ return false;
+ }
+
+ if (!ReadDmaBufMapRefs(pid, dmabufs)) {
+ LOG(ERROR) << "Failed to read dmabuf map references";
+ return false;
+ }
+ return true;
+}
+
+} // namespace dmabufinfo
+} // namespace android
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
new file mode 100644
index 0000000..eb53e57
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -0,0 +1,484 @@
+/* Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/dma-buf.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ion/ion.h>
+
+#include <dmabufinfo/dmabufinfo.h>
+
+using namespace ::android::dmabufinfo;
+using namespace ::android::base;
+
+#define MAX_HEAP_NAME 32
+#define ION_HEAP_ANY_MASK (0x7fffffff)
+
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+#ifndef DMA_BUF_SET_NAME
+#define DMA_BUF_SET_NAME _IOW(DMA_BUF_BASE, 5, const char*)
+#endif
+
+class fd_sharer {
+ public:
+ fd_sharer();
+ ~fd_sharer() { kill(); }
+
+ bool ok() const { return child_pid > 0; }
+ bool sendfd(int fd);
+ bool kill();
+ pid_t pid() const { return child_pid; }
+
+ private:
+ unique_fd parent_fd, child_fd;
+ pid_t child_pid;
+
+ void run();
+};
+
+fd_sharer::fd_sharer() : parent_fd{}, child_fd{}, child_pid{-1} {
+ bool sp_ok = android::base::Socketpair(SOCK_STREAM, &parent_fd, &child_fd);
+ if (!sp_ok) return;
+
+ child_pid = fork();
+ if (child_pid < 0) return;
+
+ if (child_pid == 0) run();
+}
+
+bool fd_sharer::kill() {
+ int err = ::kill(child_pid, SIGKILL);
+ if (err < 0) return false;
+
+ return ::waitpid(child_pid, nullptr, 0) == child_pid;
+}
+
+void fd_sharer::run() {
+ while (true) {
+ int fd;
+ char unused = 0;
+
+ iovec iov{};
+ iov.iov_base = &unused;
+ iov.iov_len = sizeof(unused);
+
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ ssize_t s = TEMP_FAILURE_RETRY(recvmsg(child_fd, &msg, 0));
+ if (s == -1) break;
+
+ s = TEMP_FAILURE_RETRY(write(child_fd, &unused, sizeof(unused)));
+ if (s == -1) break;
+ }
+}
+
+bool fd_sharer::sendfd(int fd) {
+ char unused = 0;
+
+ iovec iov{};
+ iov.iov_base = &unused;
+ iov.iov_len = sizeof(unused);
+
+ msghdr msg{};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(fd))];
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ int* fd_buf = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ *fd_buf = fd;
+
+ ssize_t s = TEMP_FAILURE_RETRY(sendmsg(parent_fd, &msg, 0));
+ if (s == -1) return false;
+
+ // The target process installs the fd into its fd table during recvmsg().
+ // So if we return now, there's a brief window between sendfd() finishing
+ // and libmemoryinfo actually seeing that the buffer has been shared. This
+ // window is just large enough to break tests.
+ //
+ // To work around this, wait for the target process to respond with a dummy
+ // byte, with a timeout of 1 s.
+ pollfd p{};
+ p.fd = parent_fd;
+ p.events = POLL_IN;
+ int ready = poll(&p, 1, 1000);
+ if (ready != 1) return false;
+
+ s = TEMP_FAILURE_RETRY(read(parent_fd, &unused, sizeof(unused)));
+ if (s == -1) return false;
+
+ return true;
+}
+
+#define EXPECT_ONE_BUF_EQ(_bufptr, _name, _fdrefs, _maprefs, _expname, _count, _size) \
+ do { \
+ EXPECT_EQ(_bufptr->name(), _name); \
+ EXPECT_EQ(_bufptr->fdrefs().size(), _fdrefs); \
+ EXPECT_EQ(_bufptr->maprefs().size(), _maprefs); \
+ EXPECT_EQ(_bufptr->exporter(), _expname); \
+ EXPECT_EQ(_bufptr->count(), _count); \
+ EXPECT_EQ(_bufptr->size(), _size); \
+ } while (0)
+
+#define EXPECT_PID_IN_FDREFS(_bufptr, _pid, _expect) \
+ do { \
+ const std::unordered_map<pid_t, int>& _fdrefs = _bufptr->fdrefs(); \
+ auto _ref = _fdrefs.find(_pid); \
+ EXPECT_EQ((_ref != _fdrefs.end()), _expect); \
+ } while (0)
+
+#define EXPECT_PID_IN_MAPREFS(_bufptr, _pid, _expect) \
+ do { \
+ const std::unordered_map<pid_t, int>& _maprefs = _bufptr->maprefs(); \
+ auto _ref = _maprefs.find(_pid); \
+ EXPECT_EQ((_ref != _maprefs.end()), _expect); \
+ } while (0)
+
+TEST(DmaBufInfoParser, TestReadDmaBufInfo) {
+ std::string bufinfo = R"bufinfo(00045056 00000002 00000007 00000002 ion 00022069
+ Attached Devices:
+Total 0 devices attached
+01048576 00000002 00000007 00000001 ion 00019834 CAMERA
+ Attached Devices:
+ soc:qcom,cam_smmu:msm_cam_smmu_icp
+Total 1 devices attached)bufinfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(bufinfo, tf.fd));
+ std::string path = std::string(tf.path);
+
+ std::vector<DmaBuffer> dmabufs;
+ EXPECT_TRUE(ReadDmaBufInfo(&dmabufs, path));
+
+ EXPECT_EQ(dmabufs.size(), 2UL);
+
+ EXPECT_EQ(dmabufs[0].size(), 45056UL);
+ EXPECT_EQ(dmabufs[0].inode(), 22069UL);
+ EXPECT_EQ(dmabufs[0].count(), 2UL);
+ EXPECT_EQ(dmabufs[0].exporter(), "ion");
+ EXPECT_TRUE(dmabufs[0].name().empty());
+ EXPECT_EQ(dmabufs[0].total_refs(), 0ULL);
+ EXPECT_TRUE(dmabufs[0].fdrefs().empty());
+ EXPECT_TRUE(dmabufs[0].maprefs().empty());
+
+ EXPECT_EQ(dmabufs[1].size(), 1048576UL);
+ EXPECT_EQ(dmabufs[1].inode(), 19834UL);
+ EXPECT_EQ(dmabufs[1].count(), 1UL);
+ EXPECT_EQ(dmabufs[1].exporter(), "ion");
+ EXPECT_FALSE(dmabufs[1].name().empty());
+ EXPECT_EQ(dmabufs[1].name(), "CAMERA");
+ EXPECT_EQ(dmabufs[1].total_refs(), 0ULL);
+ EXPECT_TRUE(dmabufs[1].fdrefs().empty());
+ EXPECT_TRUE(dmabufs[1].maprefs().empty());
+}
+
+class DmaBufTester : public ::testing::Test {
+ public:
+ DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
+
+ ~DmaBufTester() {
+ if (is_valid()) {
+ ion_close(ion_fd);
+ }
+ }
+
+ bool is_valid() { return (ion_fd >= 0 && ion_heap_mask > 0); }
+
+ unique_fd allocate(uint64_t size, const std::string& name) {
+ int fd;
+ int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
+ if (err < 0) {
+ return unique_fd{err};
+ }
+
+ if (!name.empty()) {
+ err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str());
+ if (err < 0) return unique_fd{-errno};
+ }
+
+ return unique_fd{fd};
+ }
+
+ void readAndCheckDmaBuffer(std::vector<DmaBuffer>* dmabufs, pid_t pid, const std::string name,
+ size_t fdrefs_size, size_t maprefs_size, const std::string exporter,
+ size_t refcount, uint64_t buf_size, bool expectFdrefs,
+ bool expectMapRefs) {
+ EXPECT_TRUE(ReadDmaBufInfo(pid, dmabufs));
+ EXPECT_EQ(dmabufs->size(), 1UL);
+ EXPECT_ONE_BUF_EQ(dmabufs->begin(), name, fdrefs_size, maprefs_size, exporter, refcount,
+ buf_size);
+ // Make sure the buffer has the right pid too.
+ EXPECT_PID_IN_FDREFS(dmabufs->begin(), pid, expectFdrefs);
+ EXPECT_PID_IN_MAPREFS(dmabufs->begin(), pid, expectMapRefs);
+ }
+
+ bool checkPidRef(DmaBuffer& dmabuf, pid_t pid, int expectFdrefs) {
+ int fdrefs = dmabuf.fdrefs().find(pid)->second;
+ return fdrefs == expectFdrefs;
+ }
+
+ private:
+ int get_ion_heap_mask() {
+ if (ion_fd < 0) {
+ return 0;
+ }
+
+ if (ion_is_legacy(ion_fd)) {
+ // Since ION is still in staging, we've seen that the heap mask ids are also
+ // changed across kernels for some reason. So, here we basically ask for a buffer
+ // from _any_ heap.
+ return ION_HEAP_ANY_MASK;
+ }
+
+ int cnt;
+ int err = ion_query_heap_cnt(ion_fd, &cnt);
+ if (err < 0) {
+ return err;
+ }
+
+ std::vector<ion_heap_data> heaps;
+ heaps.resize(cnt);
+ err = ion_query_get_heaps(ion_fd, cnt, &heaps[0]);
+ if (err < 0) {
+ return err;
+ }
+
+ unsigned int ret = 0;
+ for (auto& it : heaps) {
+ if (!strcmp(it.name, "ion_system_heap")) {
+ ret |= (1 << it.heap_id);
+ }
+ }
+
+ return ret;
+ }
+
+ unique_fd ion_fd;
+ const int ion_heap_mask;
+};
+
+TEST_F(DmaBufTester, TestFdRef) {
+ // Test if a dma buffer is found while the corresponding file descriptor
+ // is open
+ ASSERT_TRUE(is_valid());
+ pid_t pid = getpid();
+ std::vector<DmaBuffer> dmabufs;
+ {
+ // Allocate one buffer and make sure the library can see it
+ unique_fd buf = allocate(4096, "dmabuftester-4k");
+ ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+ EXPECT_EQ(dmabufs.size(), 1UL);
+ EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL);
+
+ // Make sure the buffer has the right pid too.
+ EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
+ }
+
+ // Now make sure the buffer has disappeared
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+ EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, TestMapRef) {
+ // Test to make sure we can find a buffer if the fd is closed but the buffer
+ // is mapped
+ ASSERT_TRUE(is_valid());
+ pid_t pid = getpid();
+ std::vector<DmaBuffer> dmabufs;
+ {
+ // Allocate one buffer and make sure the library can see it
+ unique_fd buf = allocate(4096, "dmabuftester-4k");
+ ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+ auto ptr = mmap(0, 4096, PROT_READ, MAP_SHARED, buf, 0);
+ ASSERT_NE(ptr, MAP_FAILED);
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+ EXPECT_EQ(dmabufs.size(), 1UL);
+ EXPECT_ONE_BUF_EQ(dmabufs.begin(), "dmabuftester-4k", 1UL, 1UL, "ion", 2UL, 4096ULL);
+
+ // Make sure the buffer has the right pid too.
+ EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, true);
+ EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
+
+ // close the file descriptor and re-read the stats
+ buf.reset(-1);
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+
+ EXPECT_EQ(dmabufs.size(), 1UL);
+ EXPECT_ONE_BUF_EQ(dmabufs.begin(), "<unknown>", 0UL, 1UL, "<unknown>", 0UL, 4096ULL);
+
+ EXPECT_PID_IN_FDREFS(dmabufs.begin(), pid, false);
+ EXPECT_PID_IN_MAPREFS(dmabufs.begin(), pid, true);
+
+ // unmap the bufer and lose all references
+ munmap(ptr, 4096);
+ }
+
+ // Now make sure the buffer has disappeared
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+ EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, TestSharedfd) {
+ // Each time a shared buffer is received over a socket, the remote process
+ // will take an extra reference on it.
+
+ ASSERT_TRUE(is_valid());
+
+ pid_t pid = getpid();
+ std::vector<DmaBuffer> dmabufs;
+ {
+ fd_sharer sharer{};
+ ASSERT_TRUE(sharer.ok());
+ // Allocate one buffer and make sure the library can see it
+ unique_fd buf = allocate(4096, "dmabuftester-4k");
+ ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+
+ ASSERT_TRUE(sharer.sendfd(buf));
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+ false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+ readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+ 4096ULL, true, false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 1));
+
+ ASSERT_TRUE(sharer.sendfd(buf));
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 3UL, 4096ULL, true,
+ false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+ readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 3UL,
+ 4096ULL, true, false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], sharer.pid(), 2));
+
+ ASSERT_TRUE(sharer.kill());
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+ }
+
+ // Now make sure the buffer has disappeared
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+ EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, DupFdTest) {
+ // dup()ing an fd will make this process take an extra reference on the
+ // shared buffer.
+
+ ASSERT_TRUE(is_valid());
+
+ pid_t pid = getpid();
+ std::vector<DmaBuffer> dmabufs;
+ {
+ // Allocate one buffer and make sure the library can see it
+ unique_fd buf = allocate(4096, "dmabuftester-4k");
+ ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+
+ unique_fd buf2{dup(buf)};
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+ false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 2));
+
+ close(buf2.release());
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+ EXPECT_TRUE(checkPidRef(dmabufs[0], pid, 1));
+ }
+
+ // Now make sure the buffer has disappeared
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+ EXPECT_TRUE(dmabufs.empty());
+}
+
+TEST_F(DmaBufTester, ForkTest) {
+ // fork()ing a child will cause the child to automatically take a reference
+ // on any existing shared buffers.
+ ASSERT_TRUE(is_valid());
+
+ pid_t pid = getpid();
+ std::vector<DmaBuffer> dmabufs;
+ {
+ // Allocate one buffer and make sure the library can see it
+ unique_fd buf = allocate(4096, "dmabuftester-4k");
+ ASSERT_GT(buf, 0) << "Allocated buffer is invalid";
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+ fd_sharer sharer{};
+ ASSERT_TRUE(sharer.ok());
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 2UL, 4096ULL, true,
+ false);
+ readAndCheckDmaBuffer(&dmabufs, sharer.pid(), "dmabuftester-4k", 1UL, 0UL, "ion", 2UL,
+ 4096ULL, true, false);
+ ASSERT_TRUE(sharer.kill());
+ readAndCheckDmaBuffer(&dmabufs, pid, "dmabuftester-4k", 1UL, 0UL, "ion", 1UL, 4096ULL, true,
+ false);
+ }
+
+ // Now make sure the buffer has disappeared
+ ASSERT_TRUE(ReadDmaBufInfo(pid, &dmabufs));
+ EXPECT_TRUE(dmabufs.empty());
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
new file mode 100644
index 0000000..a6e7f69
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+namespace android {
+namespace dmabufinfo {
+
+struct DmaBuffer {
+ public:
+ DmaBuffer(ino_t inode, uint64_t size, uint64_t count, const std::string& exporter,
+ const std::string& name)
+ : inode_(inode), size_(size), count_(count), exporter_(exporter), name_(name) {
+ total_refs_ = 0;
+ }
+ DmaBuffer() = default;
+ ~DmaBuffer() = default;
+
+ // Adds one file descriptor reference for the given pid
+ void AddFdRef(pid_t pid) {
+ AddRefToPidMap(pid, &fdrefs_);
+ total_refs_++;
+ }
+
+ // Adds one map reference for the given pid
+ void AddMapRef(pid_t pid) {
+ AddRefToPidMap(pid, &maprefs_);
+ total_refs_++;
+ }
+
+ // Getters for each property
+ uint64_t size() const { return size_; }
+ const std::unordered_map<pid_t, int>& fdrefs() const { return fdrefs_; }
+ const std::unordered_map<pid_t, int>& maprefs() const { return maprefs_; }
+ ino_t inode() const { return inode_; }
+ uint64_t total_refs() const { return total_refs_; }
+ uint64_t count() const { return count_; };
+ const std::set<pid_t>& pids() const { return pids_; }
+ const std::string& name() const { return name_; }
+ const std::string& exporter() const { return exporter_; }
+ void SetName(const std::string& name) { name_ = name; }
+ void SetExporter(const std::string& exporter) { exporter_ = exporter; }
+ void SetCount(uint64_t count) { count_ = count; }
+ uint64_t Pss() const { return size_ / pids_.size(); }
+
+ bool operator==(const DmaBuffer& rhs) {
+ return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) &&
+ (exporter_ == rhs.exporter());
+ }
+
+ private:
+ ino_t inode_;
+ uint64_t size_;
+ uint64_t count_;
+ uint64_t total_refs_;
+ std::set<pid_t> pids_;
+ std::string exporter_;
+ std::string name_;
+ std::unordered_map<pid_t, int> fdrefs_;
+ std::unordered_map<pid_t, int> maprefs_;
+ void AddRefToPidMap(pid_t pid, std::unordered_map<pid_t, int>* map) {
+ // The first time we find a ref, we set the ref count to 1
+ // otherwise, increment the existing ref count
+ auto [it, inserted] = map->insert(std::make_pair(pid, 1));
+ if (!inserted)
+ it->second++;
+ pids_.insert(pid);
+ }
+};
+
+// Read and return current dma buf objects from
+// DEBUGFS/dma_buf/bufinfo. The references to each dma buffer are not
+// populated here and will return an empty vector.
+// Returns false if something went wrong with the function, true otherwise.
+bool ReadDmaBufInfo(std::vector<DmaBuffer>* dmabufs,
+ const std::string& path = "/sys/kernel/debug/dma_buf/bufinfo");
+
+
+// Read and return dmabuf objects for a given process without the help
+// of DEBUGFS
+// Returns false if something went wrong with the function, true otherwise.
+bool ReadDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs);
+
+// Append new dmabuf objects from a given process to an existing vector.
+// When the vector contains an existing element with a matching inode,
+// the reference counts will be updated.
+// Does not depend on DEBUGFS.
+// Returns false if something went wrong with the function, true otherwise.
+bool AppendDmaBufInfo(pid_t pid, std::vector<DmaBuffer>* dmabufs);
+
+} // namespace dmabufinfo
+} // namespace android
diff --git a/libmeminfo/libdmabufinfo/tools/Android.bp b/libmeminfo/libdmabufinfo/tools/Android.bp
new file mode 100644
index 0000000..224b68e
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/tools/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "dmabuf_dump",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["dmabuf_dump.cpp"],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libdmabufinfo",
+ ],
+ product_specific: true,
+}
\ No newline at end of file
diff --git a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
new file mode 100644
index 0000000..48901b1
--- /dev/null
+++ b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <dmabufinfo/dmabufinfo.h>
+
+using DmaBuffer = ::android::dmabufinfo::DmaBuffer;
+
+[[noreturn]] static void usage(int exit_status) {
+ fprintf(stderr,
+ "Usage: %s [-ah] [PID] \n"
+ "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n"
+ "-h\t show this help\n"
+ " \t If PID is supplied, the dmabuf information for that process is shown.\n",
+ getprogname());
+
+ exit(exit_status);
+}
+
+static std::string GetProcessComm(const pid_t pid) {
+ std::string pid_path = android::base::StringPrintf("/proc/%d/comm", pid);
+ std::ifstream in{pid_path};
+ if (!in) return std::string("N/A");
+ std::string line;
+ std::getline(in, line);
+ if (!in) return std::string("N/A");
+ return line;
+}
+
+static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) {
+ if (bufs.empty()) {
+ printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
+ return;
+ }
+
+ // Find all unique pids in the input vector, create a set
+ std::set<pid_t> pid_set;
+ for (auto& buf : bufs) {
+ pid_set.insert(buf.pids().begin(), buf.pids().end());
+ }
+
+ // Format the header string spaced and separated with '|'
+ printf(" Dmabuf Inode | Size | Ref Counts |");
+ for (auto pid : pid_set) {
+ printf("%16s:%-5d |", GetProcessComm(pid).c_str(), pid);
+ }
+ printf("\n");
+
+ // holds per-process dmabuf size in kB
+ std::map<pid_t, uint64_t> per_pid_size = {};
+ uint64_t dmabuf_total_size = 0;
+
+ // Iterate through all dmabufs and collect per-process sizes, refs
+ for (auto& buf : bufs) {
+ printf("%16ju |%13" PRIu64 " kB |%16" PRIu64 " |", static_cast<uintmax_t>(buf.inode()),
+ buf.size() / 1024, buf.total_refs());
+ // Iterate through each process to find out per-process references for each buffer,
+ // gather total size used by each process etc.
+ for (pid_t pid : pid_set) {
+ int pid_refs = 0;
+ if (buf.fdrefs().count(pid) == 1) {
+ // Get the total number of ref counts the process is holding
+ // on this buffer. We don't differentiate between mmap or fd.
+ pid_refs += buf.fdrefs().at(pid);
+ if (buf.maprefs().count(pid) == 1) {
+ pid_refs += buf.maprefs().at(pid);
+ }
+ }
+
+ if (pid_refs) {
+ // Add up the per-pid total size. Note that if a buffer is mapped
+ // in 2 different processes, the size will be shown as mapped or opened
+ // in both processes. This is intended for visibility.
+ //
+ // If one wants to get the total *unique* dma buffers, they can simply
+ // sum the size of all dma bufs shown by the tool
+ per_pid_size[pid] += buf.size() / 1024;
+ printf("%17d refs |", pid_refs);
+ } else {
+ printf("%22s |", "--");
+ }
+ }
+ dmabuf_total_size += buf.size() / 1024;
+ printf("\n");
+ }
+
+ printf("------------------------------------\n");
+ printf("%-16s %13" PRIu64 " kB |%16s |", "TOTALS", dmabuf_total_size, "n/a");
+ for (auto pid : pid_set) {
+ printf("%19" PRIu64 " kB |", per_pid_size[pid]);
+ }
+ printf("\n");
+
+ return;
+}
+
+static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& bufs) {
+ if (bufs.empty()) {
+ printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
+ return;
+ }
+
+ // Create a reverse map from pid to dmabufs
+ std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
+ uint64_t total_size = 0; // Total size of dmabufs in the system
+ uint64_t kernel_rss = 0; // Total size of dmabufs NOT mapped or opened by a process
+ for (auto& buf : bufs) {
+ for (auto pid : buf.pids()) {
+ pid_to_inodes[pid].insert(buf.inode());
+ }
+ total_size += buf.size();
+ if (buf.fdrefs().empty() && buf.maprefs().empty()) {
+ kernel_rss += buf.size();
+ }
+ }
+ // Create an inode to dmabuf map. We know inodes are unique..
+ std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
+ for (auto buf : bufs) {
+ inode_to_dmabuf[buf.inode()] = buf;
+ }
+
+ uint64_t total_rss = 0, total_pss = 0;
+ for (auto& [pid, inodes] : pid_to_inodes) {
+ uint64_t pss = 0;
+ uint64_t rss = 0;
+
+ printf("%16s:%-5d\n", GetProcessComm(pid).c_str(), pid);
+ printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode");
+ for (auto& inode : inodes) {
+ DmaBuffer& buf = inode_to_dmabuf[inode];
+ printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n",
+ buf.name().empty() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024,
+ buf.Pss() / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode()));
+ rss += buf.size();
+ pss += buf.Pss();
+ }
+ printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024,
+ pss / 1024, "");
+ printf("----------------------\n");
+ total_rss += rss;
+ total_pss += pss;
+ }
+ printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64
+ " kB userspace_pss: %" PRIu64 " kB\n ",
+ total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024);
+}
+
+static bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
+ bufs->clear();
+
+ if (!ReadDmaBufInfo(bufs)) {
+ fprintf(stderr, "debugfs entry for dmabuf not available, skipping\n");
+ return false;
+ }
+
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
+ if (!dir) {
+ fprintf(stderr, "Failed to open /proc directory\n");
+ bufs->clear();
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ if (dent->d_type != DT_DIR) continue;
+
+ int pid = atoi(dent->d_name);
+ if (pid == 0) {
+ continue;
+ }
+
+ if (!AppendDmaBufInfo(pid, bufs)) {
+ fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid);
+ bufs->clear();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ struct option longopts[] = {{"all", no_argument, nullptr, 'a'},
+ {"help", no_argument, nullptr, 'h'},
+ {0, 0, nullptr, 0}};
+
+ int opt;
+ bool show_table = false;
+ while ((opt = getopt_long(argc, argv, "ah", longopts, nullptr)) != -1) {
+ switch (opt) {
+ case 'a':
+ show_table = true;
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ pid_t pid = -1;
+ if (optind < argc) {
+ if (show_table) {
+ fprintf(stderr, "Invalid arguments: -a does not need arguments\n");
+ usage(EXIT_FAILURE);
+ }
+ if (optind != (argc - 1)) {
+ fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n");
+ usage(EXIT_FAILURE);
+ }
+ pid = atoi(argv[optind]);
+ if (pid == 0) {
+ fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ std::vector<DmaBuffer> bufs;
+ if (pid != -1) {
+ if (!ReadDmaBufInfo(pid, &bufs)) {
+ fprintf(stderr, "Unable to read dmabuf info for %d\n", pid);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE);
+ }
+
+ // Show the old dmabuf table, inode x process
+ if (show_table) {
+ PrintDmaBufTable(bufs);
+ return 0;
+ }
+
+ PrintDmaBufPerProcess(bufs);
+
+ return 0;
+}
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
new file mode 100644
index 0000000..d88919b
--- /dev/null
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include <benchmark/benchmark.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::SmapsOrRollupFromFile;
+using ::android::meminfo::SysMemInfo;
+
+enum {
+ MEMINFO_TOTAL,
+ MEMINFO_FREE,
+ MEMINFO_BUFFERS,
+ MEMINFO_CACHED,
+ MEMINFO_SHMEM,
+ MEMINFO_SLAB,
+ MEMINFO_SLAB_RECLAIMABLE,
+ MEMINFO_SLAB_UNRECLAIMABLE,
+ MEMINFO_SWAP_TOTAL,
+ MEMINFO_SWAP_FREE,
+ MEMINFO_ZRAM_TOTAL,
+ MEMINFO_MAPPED,
+ MEMINFO_VMALLOC_USED,
+ MEMINFO_PAGE_TABLES,
+ MEMINFO_KERNEL_STACK,
+ MEMINFO_COUNT
+};
+
+static void get_mem_info(uint64_t mem[], const char* file) {
+ char buffer[4096];
+ unsigned int numFound = 0;
+
+ int fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ printf("Unable to open %s: %s\n", file, strerror(errno));
+ return;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+
+ if (len < 0) {
+ printf("Empty %s\n", file);
+ return;
+ }
+ buffer[len] = 0;
+
+ static const char* const tags[] = {
+ "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:", "Slab:",
+ "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:", "ZRam:", "Mapped:",
+ "VmallocUsed:", "PageTables:", "KernelStack:", NULL};
+
+ static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
+
+ memset(mem, 0, sizeof(uint64_t) * 15);
+ char* p = buffer;
+ while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
+ int i = 0;
+ while (tags[i]) {
+ // std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) <<
+ // std::endl;
+ if (strncmp(p, tags[i], tagsLen[i]) == 0) {
+ p += tagsLen[i];
+ while (*p == ' ') p++;
+ char* num = p;
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p != 0) {
+ *p = 0;
+ p++;
+ }
+ mem[i] = atoll(num);
+ numFound++;
+ break;
+ }
+ i++;
+ }
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ }
+}
+
+static void BM_ReadMemInfo_old(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ::android::base::WriteStringToFd(meminfo, tf.fd);
+
+ uint64_t mem[MEMINFO_COUNT];
+ for (auto _ : state) {
+ get_mem_info(mem, tf.path);
+ }
+}
+BENCHMARK(BM_ReadMemInfo_old);
+
+static void BM_ReadMemInfo_new(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ android::base::WriteStringToFd(meminfo, tf.fd);
+
+ std::string file = std::string(tf.path);
+ std::vector<uint64_t> mem(MEMINFO_COUNT);
+ const std::vector<std::string> tags = {
+ SysMemInfo::kMemTotal, SysMemInfo::kMemFree, SysMemInfo::kMemBuffers,
+ SysMemInfo::kMemCached, SysMemInfo::kMemShmem, SysMemInfo::kMemSlab,
+ SysMemInfo::kMemSReclaim, SysMemInfo::kMemSUnreclaim, SysMemInfo::kMemSwapTotal,
+ SysMemInfo::kMemSwapFree, SysMemInfo::kMemMapped, SysMemInfo::kMemVmallocUsed,
+ SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+ };
+
+ SysMemInfo smi;
+ for (auto _ : state) {
+ smi.ReadMemInfo(tags, &mem, file);
+ }
+}
+BENCHMARK(BM_ReadMemInfo_new);
+
+static uint64_t get_zram_mem_used(const std::string& zram_dir) {
+ FILE* f = fopen((zram_dir + "mm_stat").c_str(), "r");
+ if (f) {
+ uint64_t mem_used_total = 0;
+
+ int matched = fscanf(f, "%*d %*d %" SCNu64 " %*d %*d %*d %*d", &mem_used_total);
+ if (matched != 1)
+ fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mm_stat").c_str());
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ f = fopen((zram_dir + "mem_used_total").c_str(), "r");
+ if (f) {
+ uint64_t mem_used_total = 0;
+
+ int matched = fscanf(f, "%" SCNu64, &mem_used_total);
+ if (matched != 1)
+ fprintf(stderr, "warning: failed to parse %s\n", (zram_dir + "mem_used_total").c_str());
+
+ fclose(f);
+ return mem_used_total;
+ }
+
+ return 0;
+}
+
+static void BM_ZramTotal_old(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ for (auto _ : state) {
+ uint64_t zram_total __attribute__((unused)) = get_zram_mem_used(zram_mmstat_dir) / 1024;
+ }
+}
+BENCHMARK(BM_ZramTotal_old);
+
+static void BM_ZramTotal_new(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ SysMemInfo smi;
+ for (auto _ : state) {
+ uint64_t zram_total __attribute__((unused)) = smi.mem_zram_kb(zram_mmstat_dir);
+ }
+}
+BENCHMARK(BM_ZramTotal_new);
+
+static void BM_MemInfoWithZram_old(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ::android::base::WriteStringToFd(meminfo, tf.fd);
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ uint64_t mem[MEMINFO_COUNT];
+ for (auto _ : state) {
+ get_mem_info(mem, tf.path);
+ mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used("/sys/block/zram0/") / 1024;
+ CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+ }
+}
+BENCHMARK(BM_MemInfoWithZram_old);
+
+static void BM_MemInfoWithZram_new(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ android::base::WriteStringToFd(meminfo, tf.fd);
+
+ std::string file = std::string(tf.path);
+ std::vector<uint64_t> mem(MEMINFO_COUNT);
+ std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+ auto it = tags.begin();
+ tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+ SysMemInfo smi;
+
+ for (auto _ : state) {
+ smi.ReadMemInfo(tags, &mem, file);
+ CHECK_EQ(mem[MEMINFO_KERNEL_STACK], 4880u);
+ }
+}
+BENCHMARK(BM_MemInfoWithZram_new);
+
+// Current implementation is in frameworks/base/core/jni/android_os_Debug.cpp.
+// That implementation is still buggy and it skips over vmalloc allocated memory by kernel modules.
+// This is the *fixed* version of the same implementation intended for benchmarking against the new
+// one.
+static uint64_t get_allocated_vmalloc_memory(const std::string& vm_file) {
+ char line[1024];
+
+ uint64_t vmalloc_allocated_size = 0;
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(vm_file.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ return 0;
+ }
+
+ while (true) {
+ if (fgets(line, 1024, fp.get()) == NULL) {
+ break;
+ }
+
+ // check to see if there are pages mapped in vmalloc area
+ if (!strstr(line, "pages=")) {
+ continue;
+ }
+
+ long nr_pages;
+ if (sscanf(line, "%*x-%*x %*ld %*s pages=%ld", &nr_pages) == 1) {
+ vmalloc_allocated_size += (nr_pages * getpagesize());
+ } else if (sscanf(line, "%*x-%*x %*ld %*s %*s pages=%ld", &nr_pages) == 1) {
+ // The second case is for kernel modules. If allocation comes from the module,
+ // kernel puts an extra string containing the module name before "pages=" in
+ // the line.
+ // See: https://elixir.bootlin.com/linux/latest/source/kernel/kallsyms.c#L373
+ vmalloc_allocated_size += (nr_pages * getpagesize());
+ }
+ }
+ return vmalloc_allocated_size;
+}
+
+static void BM_VmallocInfo_old_fixed(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string vmallocinfo =
+ ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+ for (auto _ : state) {
+ CHECK_EQ(get_allocated_vmalloc_memory(vmallocinfo), 29884416);
+ }
+}
+BENCHMARK(BM_VmallocInfo_old_fixed);
+
+static void BM_VmallocInfo_new(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string vmallocinfo =
+ ::android::base::StringPrintf("%s/testdata1/vmallocinfo", exec_dir.c_str());
+ for (auto _ : state) {
+ CHECK_EQ(::android::meminfo::ReadVmallocInfo(vmallocinfo), 29884416);
+ }
+}
+BENCHMARK(BM_VmallocInfo_new);
+
+// This implementation is picked up as-is from frameworks/base/core/jni/android_os_Debug.cpp
+// and only slightly modified to use std:unique_ptr.
+static bool get_smaps_rollup(const std::string path, MemUsage* rollup) {
+ char lineBuffer[1024];
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp != nullptr) {
+ char* line;
+ while (true) {
+ if (fgets(lineBuffer, sizeof(lineBuffer), fp.get()) == NULL) {
+ break;
+ }
+ line = lineBuffer;
+
+ switch (line[0]) {
+ case 'P':
+ if (strncmp(line, "Pss:", 4) == 0) {
+ char* c = line + 4;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ rollup->pss += atoi(c);
+ } else if (strncmp(line, "Private_Clean:", 14) == 0 ||
+ strncmp(line, "Private_Dirty:", 14) == 0) {
+ char* c = line + 14;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ rollup->uss += atoi(c);
+ }
+ break;
+ case 'R':
+ if (strncmp(line, "Rss:", 4) == 0) {
+ char* c = line + 4;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ rollup->rss += atoi(c);
+ }
+ break;
+ case 'S':
+ if (strncmp(line, "SwapPss:", 8) == 0) {
+ char* c = line + 8;
+ long lSwapPss;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ lSwapPss = atoi(c);
+ rollup->swap_pss += lSwapPss;
+ }
+ break;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+static void BM_SmapsRollup_old(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
+ for (auto _ : state) {
+ MemUsage stats;
+ CHECK_EQ(get_smaps_rollup(path, &stats), true);
+ CHECK_EQ(stats.pss, 108384);
+ }
+}
+BENCHMARK(BM_SmapsRollup_old);
+
+static void BM_SmapsRollup_new(benchmark::State& state) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string path = ::android::base::StringPrintf("%s/testdata1/smaps", exec_dir.c_str());
+ for (auto _ : state) {
+ MemUsage stats;
+ CHECK_EQ(SmapsOrRollupFromFile(path, &stats), true);
+ CHECK_EQ(stats.pss, 108384);
+ }
+}
+BENCHMARK(BM_SmapsRollup_new);
+
+BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
new file mode 100644
index 0000000..4c2be91
--- /dev/null
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+using namespace std;
+using namespace android::meminfo;
+
+pid_t pid = -1;
+
+TEST(ProcMemInfo, TestWorkingTestReset) {
+ // Expect reset to succeed
+ EXPECT_TRUE(ProcMemInfo::ResetWorkingSet(pid));
+}
+
+TEST(ProcMemInfo, UsageEmpty) {
+ // If we created the object for getting working set,
+ // the usage must be empty
+ ProcMemInfo proc_mem(pid, true);
+ const MemUsage& usage = proc_mem.Usage();
+ EXPECT_EQ(usage.rss, 0);
+ EXPECT_EQ(usage.vss, 0);
+ EXPECT_EQ(usage.pss, 0);
+ EXPECT_EQ(usage.uss, 0);
+ EXPECT_EQ(usage.swap, 0);
+}
+
+TEST(ProcMemInfo, MapsNotEmpty) {
+ // Make sure the process maps are never empty
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.Maps();
+ EXPECT_FALSE(maps.empty());
+}
+
+TEST(ProcMemInfo, MapsUsageNotEmpty) {
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.Maps();
+ EXPECT_FALSE(maps.empty());
+ uint64_t total_pss = 0;
+ uint64_t total_rss = 0;
+ uint64_t total_uss = 0;
+ for (auto& map : maps) {
+ ASSERT_NE(0, map.usage.vss);
+ total_rss += map.usage.rss;
+ total_pss += map.usage.pss;
+ total_uss += map.usage.uss;
+ }
+
+ // Crude check that stats are actually being read.
+ EXPECT_NE(0, total_rss) << "RSS zero for all maps, that is not possible.";
+ EXPECT_NE(0, total_pss) << "PSS zero for all maps, that is not possible.";
+ EXPECT_NE(0, total_uss) << "USS zero for all maps, that is not possible.";
+}
+
+TEST(ProcMemInfo, MapsUsageEmpty) {
+ ProcMemInfo proc_mem(pid);
+ const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ EXPECT_FALSE(maps.empty());
+ // Verify that all usage stats are zero in every map.
+ for (auto& map : maps) {
+ ASSERT_EQ(0, map.usage.vss);
+ ASSERT_EQ(0, map.usage.rss);
+ ASSERT_EQ(0, map.usage.pss);
+ ASSERT_EQ(0, map.usage.uss);
+ ASSERT_EQ(0, map.usage.swap);
+ ASSERT_EQ(0, map.usage.swap_pss);
+ ASSERT_EQ(0, map.usage.private_clean);
+ ASSERT_EQ(0, map.usage.private_dirty);
+ ASSERT_EQ(0, map.usage.shared_clean);
+ ASSERT_EQ(0, map.usage.shared_dirty);
+ }
+}
+
+TEST(ProcMemInfo, PageMapPresent) {
+ static constexpr size_t kNumPages = 20;
+ size_t pagesize = getpagesize();
+ void* ptr = mmap(nullptr, pagesize * (kNumPages + 2), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, ptr);
+
+ // Unmap the first page and the last page so that we guarantee this
+ // map is in a map by itself.
+ ASSERT_EQ(0, munmap(ptr, pagesize));
+ uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + pagesize;
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr + kNumPages * pagesize), pagesize));
+
+ ProcMemInfo proc_mem(getpid());
+ const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+ ASSERT_FALSE(maps.empty());
+
+ // Find the vma associated with our previously created map.
+ const Vma* test_vma = nullptr;
+ for (const Vma& vma : maps) {
+ if (vma.start == addr) {
+ test_vma = &vma;
+ break;
+ }
+ }
+ ASSERT_TRUE(test_vma != nullptr) << "Cannot find test map.";
+
+ // Verify that none of the pages are listed as present.
+ std::vector<uint64_t> pagemap;
+ ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+ ASSERT_EQ(kNumPages, pagemap.size());
+ for (size_t i = 0; i < pagemap.size(); i++) {
+ EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is present and it should not be.";
+ }
+
+ // Make some of the pages present and verify that we see them
+ // as present.
+ uint8_t* data = reinterpret_cast<uint8_t*>(addr);
+ data[0] = 1;
+ data[pagesize * 5] = 1;
+ data[pagesize * 11] = 1;
+
+ ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+ ASSERT_EQ(kNumPages, pagemap.size());
+ for (size_t i = 0; i < pagemap.size(); i++) {
+ if (i == 0 || i == 5 || i == 11) {
+ EXPECT_TRUE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is not present and it should be.";
+ } else {
+ EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+ << "Page " << i << " is present and it should not be.";
+ }
+ }
+
+ ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr), kNumPages * pagesize));
+}
+
+TEST(ProcMemInfo, WssEmpty) {
+ // If we created the object for getting usage,
+ // the working set must be empty
+ ProcMemInfo proc_mem(pid, false);
+ const MemUsage& wss = proc_mem.Wss();
+ EXPECT_EQ(wss.rss, 0);
+ EXPECT_EQ(wss.vss, 0);
+ EXPECT_EQ(wss.pss, 0);
+ EXPECT_EQ(wss.uss, 0);
+ EXPECT_EQ(wss.swap, 0);
+}
+
+TEST(ProcMemInfo, SwapOffsetsEmpty) {
+ // If we created the object for getting working set,
+ // the swap offsets must be empty
+ ProcMemInfo proc_mem(pid, true);
+ const std::vector<uint16_t>& swap_offsets = proc_mem.SwapOffsets();
+ EXPECT_EQ(swap_offsets.size(), 0);
+}
+
+TEST(ProcMemInfo, IsSmapsSupportedTest) {
+ // Get any pid and check if /proc/<pid>/smaps_rollup exists using the API.
+ // The API must return the appropriate value regardless of the after it succeeds
+ // once.
+ std::string path = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
+ bool supported = IsSmapsRollupSupported(pid);
+ EXPECT_EQ(!access(path.c_str(), F_OK | R_OK), supported);
+ // Second call must return what the first one returned regardless of the pid parameter.
+ // So, deliberately pass invalid pid.
+ EXPECT_EQ(supported, IsSmapsRollupSupported(-1));
+}
+
+TEST(ProcMemInfo, SmapsOrRollupTest) {
+ // Make sure we can parse 'smaps_rollup' correctly
+ std::string rollup =
+ R"rollup(12c00000-7fe859e000 ---p 00000000 00:00 0 [rollup]
+Rss: 331908 kB
+Pss: 202052 kB
+Shared_Clean: 158492 kB
+Shared_Dirty: 18928 kB
+Private_Clean: 90472 kB
+Private_Dirty: 64016 kB
+Referenced: 318700 kB
+Anonymous: 81984 kB
+AnonHugePages: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 5344 kB
+SwapPss: 442 kB
+Locked: 1523537 kB)rollup";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(rollup, tf.fd));
+
+ MemUsage stats;
+ ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
+ EXPECT_EQ(stats.rss, 331908);
+ EXPECT_EQ(stats.pss, 202052);
+ EXPECT_EQ(stats.uss, 154488);
+ EXPECT_EQ(stats.private_clean, 90472);
+ EXPECT_EQ(stats.private_dirty, 64016);
+ EXPECT_EQ(stats.swap_pss, 442);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupSmapsTest) {
+ // Make sure /proc/<pid>/smaps is parsed correctly
+ std::string smaps =
+ R"smaps(12c00000-13440000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 8448 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2652 kB
+Pss: 2652 kB
+Shared_Clean: 840 kB
+Shared_Dirty: 40 kB
+Private_Clean: 84 kB
+Private_Dirty: 2652 kB
+Referenced: 2652 kB
+Anonymous: 2652 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 102 kB
+SwapPss: 70 kB
+Locked: 2652 kB
+VmFlags: rd wr mr mw me ac
+)smaps";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
+
+ MemUsage stats;
+ ASSERT_EQ(SmapsOrRollupFromFile(tf.path, &stats), true);
+ EXPECT_EQ(stats.rss, 2652);
+ EXPECT_EQ(stats.pss, 2652);
+ EXPECT_EQ(stats.uss, 2736);
+ EXPECT_EQ(stats.private_clean, 84);
+ EXPECT_EQ(stats.private_dirty, 2652);
+ EXPECT_EQ(stats.swap_pss, 70);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupPssRollupTest) {
+ // Make sure /proc/<pid>/smaps is parsed correctly
+ // to get the PSS
+ std::string smaps =
+ R"smaps(12c00000-13440000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 8448 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2652 kB
+Pss: 2652 kB
+Shared_Clean: 840 kB
+Shared_Dirty: 40 kB
+Private_Clean: 84 kB
+Private_Dirty: 2652 kB
+Referenced: 2652 kB
+Anonymous: 2652 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 102 kB
+SwapPss: 70 kB
+Locked: 2652 kB
+VmFlags: rd wr mr mw me ac
+)smaps";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(smaps, tf.fd));
+
+ uint64_t pss;
+ ASSERT_EQ(SmapsOrRollupPssFromFile(tf.path, &pss), true);
+ EXPECT_EQ(pss, 2652);
+}
+
+TEST(ProcMemInfo, SmapsOrRollupPssSmapsTest) {
+ // Correctly parse smaps file to gather pss
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+
+ uint64_t pss;
+ ASSERT_EQ(SmapsOrRollupPssFromFile(path, &pss), true);
+ EXPECT_EQ(pss, 19119);
+}
+
+TEST(ProcMemInfo, ForEachVmaFromFileTest) {
+ // Parse smaps file correctly to make callbacks for each virtual memory area (vma)
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+ ProcMemInfo proc_mem(pid);
+
+ std::vector<Vma> vmas;
+ auto collect_vmas = [&](const Vma& v) { vmas.push_back(v); };
+ ASSERT_TRUE(ForEachVmaFromFile(path, collect_vmas));
+
+ // We should get a total of 6 vmas
+ ASSERT_EQ(vmas.size(), 6);
+
+ // Expect values to be equal to what we have in testdata1/smaps_short
+ // Check for sizes first
+ ASSERT_EQ(vmas[0].usage.vss, 32768);
+ EXPECT_EQ(vmas[1].usage.vss, 11204);
+ EXPECT_EQ(vmas[2].usage.vss, 16896);
+ EXPECT_EQ(vmas[3].usage.vss, 260);
+ EXPECT_EQ(vmas[4].usage.vss, 6060);
+ EXPECT_EQ(vmas[5].usage.vss, 4);
+
+ // Check for names
+ EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
+ EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
+ EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+ EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
+ EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
+ EXPECT_EQ(vmas[5].name, "[vsyscall]");
+
+ EXPECT_EQ(vmas[0].usage.rss, 2048);
+ EXPECT_EQ(vmas[1].usage.rss, 11188);
+ EXPECT_EQ(vmas[2].usage.rss, 15272);
+ EXPECT_EQ(vmas[3].usage.rss, 260);
+ EXPECT_EQ(vmas[4].usage.rss, 4132);
+ EXPECT_EQ(vmas[5].usage.rss, 0);
+
+ EXPECT_EQ(vmas[0].usage.pss, 113);
+ EXPECT_EQ(vmas[1].usage.pss, 2200);
+ EXPECT_EQ(vmas[2].usage.pss, 15272);
+ EXPECT_EQ(vmas[3].usage.pss, 260);
+ EXPECT_EQ(vmas[4].usage.pss, 1274);
+ EXPECT_EQ(vmas[5].usage.pss, 0);
+
+ EXPECT_EQ(vmas[0].usage.uss, 0);
+ EXPECT_EQ(vmas[1].usage.uss, 1660);
+ EXPECT_EQ(vmas[2].usage.uss, 15272);
+ EXPECT_EQ(vmas[3].usage.uss, 260);
+ EXPECT_EQ(vmas[4].usage.uss, 0);
+ EXPECT_EQ(vmas[5].usage.uss, 0);
+
+ EXPECT_EQ(vmas[0].usage.private_clean, 0);
+ EXPECT_EQ(vmas[1].usage.private_clean, 0);
+ EXPECT_EQ(vmas[2].usage.private_clean, 0);
+ EXPECT_EQ(vmas[3].usage.private_clean, 260);
+ EXPECT_EQ(vmas[4].usage.private_clean, 0);
+ EXPECT_EQ(vmas[5].usage.private_clean, 0);
+
+ EXPECT_EQ(vmas[0].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
+ EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
+ EXPECT_EQ(vmas[3].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[4].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[5].usage.private_dirty, 0);
+
+ EXPECT_EQ(vmas[0].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[1].usage.shared_clean, 80);
+ EXPECT_EQ(vmas[2].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[3].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
+ EXPECT_EQ(vmas[5].usage.shared_clean, 0);
+
+ EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
+ EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
+ EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[5].usage.shared_dirty, 0);
+
+ EXPECT_EQ(vmas[0].usage.swap, 0);
+ EXPECT_EQ(vmas[1].usage.swap, 0);
+ EXPECT_EQ(vmas[2].usage.swap, 0);
+ EXPECT_EQ(vmas[3].usage.swap, 0);
+ EXPECT_EQ(vmas[4].usage.swap, 0);
+ EXPECT_EQ(vmas[5].usage.swap, 0);
+
+ EXPECT_EQ(vmas[0].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[1].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[2].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[3].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[4].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[5].usage.swap_pss, 0);
+}
+
+TEST(ProcMemInfo, SmapsReturnTest) {
+ // Make sure Smaps() is never empty for any process
+ ProcMemInfo proc_mem(pid);
+ auto vmas = proc_mem.Smaps();
+ EXPECT_FALSE(vmas.empty());
+}
+
+TEST(ProcMemInfo, SmapsTest) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+ std::string path = ::android::base::StringPrintf("%s/testdata1/smaps_short", exec_dir.c_str());
+ ProcMemInfo proc_mem(pid);
+ auto vmas = proc_mem.Smaps(path);
+
+ ASSERT_FALSE(vmas.empty());
+ // We should get a total of 6 vmas
+ ASSERT_EQ(vmas.size(), 6);
+
+ // Expect values to be equal to what we have in testdata1/smaps_short
+ // Check for sizes first
+ ASSERT_EQ(vmas[0].usage.vss, 32768);
+ EXPECT_EQ(vmas[1].usage.vss, 11204);
+ EXPECT_EQ(vmas[2].usage.vss, 16896);
+ EXPECT_EQ(vmas[3].usage.vss, 260);
+ EXPECT_EQ(vmas[4].usage.vss, 6060);
+ EXPECT_EQ(vmas[5].usage.vss, 4);
+
+ // Check for names
+ EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
+ EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
+ EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+ EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
+ EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
+ EXPECT_EQ(vmas[5].name, "[vsyscall]");
+
+ EXPECT_EQ(vmas[0].usage.rss, 2048);
+ EXPECT_EQ(vmas[1].usage.rss, 11188);
+ EXPECT_EQ(vmas[2].usage.rss, 15272);
+ EXPECT_EQ(vmas[3].usage.rss, 260);
+ EXPECT_EQ(vmas[4].usage.rss, 4132);
+ EXPECT_EQ(vmas[5].usage.rss, 0);
+
+ EXPECT_EQ(vmas[0].usage.pss, 113);
+ EXPECT_EQ(vmas[1].usage.pss, 2200);
+ EXPECT_EQ(vmas[2].usage.pss, 15272);
+ EXPECT_EQ(vmas[3].usage.pss, 260);
+ EXPECT_EQ(vmas[4].usage.pss, 1274);
+ EXPECT_EQ(vmas[5].usage.pss, 0);
+
+ EXPECT_EQ(vmas[0].usage.uss, 0);
+ EXPECT_EQ(vmas[1].usage.uss, 1660);
+ EXPECT_EQ(vmas[2].usage.uss, 15272);
+ EXPECT_EQ(vmas[3].usage.uss, 260);
+ EXPECT_EQ(vmas[4].usage.uss, 0);
+ EXPECT_EQ(vmas[5].usage.uss, 0);
+
+ EXPECT_EQ(vmas[0].usage.private_clean, 0);
+ EXPECT_EQ(vmas[1].usage.private_clean, 0);
+ EXPECT_EQ(vmas[2].usage.private_clean, 0);
+ EXPECT_EQ(vmas[3].usage.private_clean, 260);
+ EXPECT_EQ(vmas[4].usage.private_clean, 0);
+ EXPECT_EQ(vmas[5].usage.private_clean, 0);
+
+ EXPECT_EQ(vmas[0].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[1].usage.private_dirty, 1660);
+ EXPECT_EQ(vmas[2].usage.private_dirty, 15272);
+ EXPECT_EQ(vmas[3].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[4].usage.private_dirty, 0);
+ EXPECT_EQ(vmas[5].usage.private_dirty, 0);
+
+ EXPECT_EQ(vmas[0].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[1].usage.shared_clean, 80);
+ EXPECT_EQ(vmas[2].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[3].usage.shared_clean, 0);
+ EXPECT_EQ(vmas[4].usage.shared_clean, 4132);
+ EXPECT_EQ(vmas[5].usage.shared_clean, 0);
+
+ EXPECT_EQ(vmas[0].usage.shared_dirty, 2048);
+ EXPECT_EQ(vmas[1].usage.shared_dirty, 9448);
+ EXPECT_EQ(vmas[2].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[3].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[4].usage.shared_dirty, 0);
+ EXPECT_EQ(vmas[5].usage.shared_dirty, 0);
+
+ EXPECT_EQ(vmas[0].usage.swap, 0);
+ EXPECT_EQ(vmas[1].usage.swap, 0);
+ EXPECT_EQ(vmas[2].usage.swap, 0);
+ EXPECT_EQ(vmas[3].usage.swap, 0);
+ EXPECT_EQ(vmas[4].usage.swap, 0);
+ EXPECT_EQ(vmas[5].usage.swap, 0);
+
+ EXPECT_EQ(vmas[0].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[1].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[2].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[3].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[4].usage.swap_pss, 0);
+ EXPECT_EQ(vmas[5].usage.swap_pss, 0);
+}
+
+TEST(SysMemInfo, TestSysMemInfoFile) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 32768 kB
+SwapFree: 4096 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 65536 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+
+ SysMemInfo mi;
+ ASSERT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 3019740);
+ EXPECT_EQ(mi.mem_free_kb(), 1809728);
+ EXPECT_EQ(mi.mem_buffers_kb(), 54736);
+ EXPECT_EQ(mi.mem_cached_kb(), 776052);
+ EXPECT_EQ(mi.mem_shmem_kb(), 4020);
+ EXPECT_EQ(mi.mem_slab_kb(), 86464);
+ EXPECT_EQ(mi.mem_slab_reclaimable_kb(), 44432);
+ EXPECT_EQ(mi.mem_slab_unreclaimable_kb(), 42032);
+ EXPECT_EQ(mi.mem_swap_kb(), 32768);
+ EXPECT_EQ(mi.mem_swap_free_kb(), 4096);
+ EXPECT_EQ(mi.mem_mapped_kb(), 62624);
+ EXPECT_EQ(mi.mem_vmalloc_used_kb(), 65536);
+ EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+ EXPECT_EQ(mi.mem_kernel_stack_kb(), 4880);
+}
+
+TEST(SysMemInfo, TestEmptyFile) {
+ TemporaryFile tf;
+ std::string empty_string = "";
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
+
+ SysMemInfo mi;
+ EXPECT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 0);
+}
+
+TEST(SysMemInfo, TestZramTotal) {
+ std::string exec_dir = ::android::base::GetExecutableDirectory();
+
+ SysMemInfo mi;
+ std::string zram_mmstat_dir = exec_dir + "/testdata1/";
+ EXPECT_EQ(mi.mem_zram_kb(zram_mmstat_dir), 30504);
+
+ std::string zram_memused_dir = exec_dir + "/testdata2/";
+ EXPECT_EQ(mi.mem_zram_kb(zram_memused_dir), 30504);
+}
+
+enum {
+ MEMINFO_TOTAL,
+ MEMINFO_FREE,
+ MEMINFO_BUFFERS,
+ MEMINFO_CACHED,
+ MEMINFO_SHMEM,
+ MEMINFO_SLAB,
+ MEMINFO_SLAB_RECLAIMABLE,
+ MEMINFO_SLAB_UNRECLAIMABLE,
+ MEMINFO_SWAP_TOTAL,
+ MEMINFO_SWAP_FREE,
+ MEMINFO_ZRAM_TOTAL,
+ MEMINFO_MAPPED,
+ MEMINFO_VMALLOC_USED,
+ MEMINFO_PAGE_TABLES,
+ MEMINFO_KERNEL_STACK,
+ MEMINFO_COUNT
+};
+
+TEST(SysMemInfo, TestZramWithTags) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 32768 kB
+SwapFree: 4096 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 65536 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+ std::string file = std::string(tf.path);
+ std::vector<uint64_t> mem(MEMINFO_COUNT);
+ std::vector<std::string> tags(SysMemInfo::kDefaultSysMemInfoTags);
+ auto it = tags.begin();
+ tags.insert(it + MEMINFO_ZRAM_TOTAL, "Zram:");
+ SysMemInfo mi;
+
+ // Read system memory info
+ EXPECT_TRUE(mi.ReadMemInfo(tags, &mem, file));
+
+ EXPECT_EQ(mem[MEMINFO_TOTAL], 3019740);
+ EXPECT_EQ(mem[MEMINFO_FREE], 1809728);
+ EXPECT_EQ(mem[MEMINFO_BUFFERS], 54736);
+ EXPECT_EQ(mem[MEMINFO_CACHED], 776052);
+ EXPECT_EQ(mem[MEMINFO_SHMEM], 4020);
+ EXPECT_EQ(mem[MEMINFO_SLAB], 86464);
+ EXPECT_EQ(mem[MEMINFO_SLAB_RECLAIMABLE], 44432);
+ EXPECT_EQ(mem[MEMINFO_SLAB_UNRECLAIMABLE], 42032);
+ EXPECT_EQ(mem[MEMINFO_SWAP_TOTAL], 32768);
+ EXPECT_EQ(mem[MEMINFO_SWAP_FREE], 4096);
+ EXPECT_EQ(mem[MEMINFO_MAPPED], 62624);
+ EXPECT_EQ(mem[MEMINFO_VMALLOC_USED], 65536);
+ EXPECT_EQ(mem[MEMINFO_PAGE_TABLES], 2900);
+ EXPECT_EQ(mem[MEMINFO_KERNEL_STACK], 4880);
+}
+
+TEST(SysMemInfo, TestVmallocInfoNoMemory) {
+ std::string vmallocinfo =
+ R"vmallocinfo(0x0000000000000000-0x0000000000000000 69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap)vmallocinfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+ std::string file = std::string(tf.path);
+
+ EXPECT_EQ(ReadVmallocInfo(file), 0);
+}
+
+TEST(SysMemInfo, TestVmallocInfoKernel) {
+ std::string vmallocinfo =
+ R"vmallocinfo(0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc)vmallocinfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+ std::string file = std::string(tf.path);
+
+ EXPECT_EQ(ReadVmallocInfo(file), getpagesize());
+}
+
+TEST(SysMemInfo, TestVmallocInfoModule) {
+ std::string vmallocinfo =
+ R"vmallocinfo(0x0000000000000000-0x0000000000000000 28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+ std::string file = std::string(tf.path);
+
+ EXPECT_EQ(ReadVmallocInfo(file), 6 * getpagesize());
+}
+
+TEST(SysMemInfo, TestVmallocInfoAll) {
+ std::string vmallocinfo =
+ R"vmallocinfo(0x0000000000000000-0x0000000000000000 69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc)vmallocinfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(vmallocinfo, tf.fd));
+ std::string file = std::string(tf.path);
+
+ EXPECT_EQ(ReadVmallocInfo(file), 7 * getpagesize());
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ pid = getpid();
+ return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/meminfo_private.h b/libmeminfo/meminfo_private.h
new file mode 100644
index 0000000..df5699c
--- /dev/null
+++ b/libmeminfo/meminfo_private.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+// Macros to do per-page flag manipulation
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGE_PFN(x) (_BITS(x, 0, 55))
+#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
new file mode 100644
index 0000000..cb17af8
--- /dev/null
+++ b/libmeminfo/pageacct.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+using unique_fd = ::android::base::unique_fd;
+
+namespace android {
+namespace meminfo {
+
+static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
+ return static_cast<off64_t>((pfn >> 6) << 3);
+}
+
+uint64_t pagesize(void) {
+ static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
+ return pagesize;
+}
+
+bool PageAcct::InitPageAcct(bool pageidle_enable) {
+ if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
+ LOG(ERROR) << "Idle page tracking is not supported by the kernel";
+ return false;
+ }
+
+ if (kpagecount_fd_ < 0) {
+ unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
+ if (count_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpagecount";
+ return false;
+ }
+ kpagecount_fd_ = std::move(count_fd);
+ }
+
+ if (kpageflags_fd_ < 0) {
+ unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
+ if (flags_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpageflags";
+ return false;
+ }
+ kpageflags_fd_ = std::move(flags_fd);
+ }
+
+ if (pageidle_enable && pageidle_fd_ < 0) {
+ unique_fd idle_fd(
+ TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
+ if (idle_fd < 0) {
+ PLOG(ERROR) << "Failed to open page idle bitmap";
+ return false;
+ }
+ pageidle_fd_ = std::move(idle_fd);
+ }
+
+ return true;
+}
+
+bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
+ if (!flags) return false;
+
+ if (kpageflags_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+ sizeof(uint64_t)) {
+ PLOG(ERROR) << "Failed to read page flags for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
+ if (!mapcount) return false;
+
+ if (kpagecount_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) !=
+ sizeof(uint64_t)) {
+ PLOG(ERROR) << "Failed to read map count for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+int PageAcct::IsPageIdle(uint64_t pfn) {
+ if (pageidle_fd_ < 0) {
+ if (!InitPageAcct(true)) return -EOPNOTSUPP;
+ }
+
+ int idle_status = MarkPageIdle(pfn);
+ if (idle_status) return idle_status;
+
+ return GetPageIdle(pfn);
+}
+
+int PageAcct::MarkPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ // set the bit corresponding to page frame
+ uint64_t idle_bits = 1ULL << (pfn % 64);
+
+ if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return 0;
+}
+
+int PageAcct::GetPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ uint64_t idle_bits;
+
+ if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) != sizeof(uint64_t)) {
+ PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return !!(idle_bits & (1ULL << (pfn % 64)));
+}
+
+// Public methods
+bool page_present(uint64_t pagemap_val) {
+ return PAGE_PRESENT(pagemap_val);
+}
+
+bool page_swapped(uint64_t pagemap_val) {
+ return PAGE_SWAPPED(pagemap_val);
+}
+
+uint64_t page_pfn(uint64_t pagemap_val) {
+ return PAGE_PFN(pagemap_val);
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
new file mode 100644
index 0000000..6f68ab4
--- /dev/null
+++ b/libmeminfo/procmeminfo.cpp
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+ to->vss += from.vss;
+ to->rss += from.rss;
+ to->pss += from.pss;
+ to->uss += from.uss;
+
+ to->swap += from.swap;
+
+ to->private_clean += from.private_clean;
+ to->private_dirty += from.private_dirty;
+
+ to->shared_clean += from.shared_clean;
+ to->shared_dirty += from.shared_dirty;
+}
+
+// Returns true if the line was valid smaps stats line false otherwise.
+static bool parse_smaps_field(const char* line, MemUsage* stats) {
+ char field[64];
+ int len;
+ if (sscanf(line, "%63s %n", field, &len) == 1 && *field && field[strlen(field) - 1] == ':') {
+ const char* c = line + len;
+ switch (field[0]) {
+ case 'P':
+ if (strncmp(field, "Pss:", 4) == 0) {
+ stats->pss = strtoull(c, nullptr, 10);
+ } else if (strncmp(field, "Private_Clean:", 14) == 0) {
+ uint64_t prcl = strtoull(c, nullptr, 10);
+ stats->private_clean = prcl;
+ stats->uss += prcl;
+ } else if (strncmp(field, "Private_Dirty:", 14) == 0) {
+ uint64_t prdi = strtoull(c, nullptr, 10);
+ stats->private_dirty = prdi;
+ stats->uss += prdi;
+ }
+ break;
+ case 'S':
+ if (strncmp(field, "Size:", 5) == 0) {
+ stats->vss = strtoull(c, nullptr, 10);
+ } else if (strncmp(field, "Shared_Clean:", 13) == 0) {
+ stats->shared_clean = strtoull(c, nullptr, 10);
+ } else if (strncmp(field, "Shared_Dirty:", 13) == 0) {
+ stats->shared_dirty = strtoull(c, nullptr, 10);
+ } else if (strncmp(field, "Swap:", 5) == 0) {
+ stats->swap = strtoull(c, nullptr, 10);
+ } else if (strncmp(field, "SwapPss:", 8) == 0) {
+ stats->swap_pss = strtoull(c, nullptr, 10);
+ }
+ break;
+ case 'R':
+ if (strncmp(field, "Rss:", 4) == 0) {
+ stats->rss = strtoull(c, nullptr, 10);
+ }
+ break;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool ProcMemInfo::ResetWorkingSet(pid_t pid) {
+ std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid);
+ if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
+ PLOG(ERROR) << "Failed to write to " << clear_refs_path;
+ return false;
+ }
+
+ return true;
+}
+
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss, uint64_t pgflags, uint64_t pgflags_mask)
+ : pid_(pid), get_wss_(get_wss), pgflags_(pgflags), pgflags_mask_(pgflags_mask) {}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+ if (maps_.empty() && !ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to read maps for Process " << pid_;
+ }
+
+ return maps_;
+}
+
+const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
+ if (maps_.empty() && !ReadMaps(get_wss_, true)) {
+ LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
+ }
+
+ return maps_;
+}
+
+const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
+ if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
+ LOG(ERROR) << "Failed to read maps for Process " << pid_;
+ }
+
+ return maps_;
+}
+
+const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
+ if (!maps_.empty()) {
+ return maps_;
+ }
+
+ auto collect_vmas = [&](const Vma& vma) { maps_.emplace_back(vma); };
+ if (path.empty() && !ForEachVma(collect_vmas)) {
+ LOG(ERROR) << "Failed to read smaps for Process " << pid_;
+ maps_.clear();
+ }
+
+ if (!path.empty() && !ForEachVmaFromFile(path, collect_vmas)) {
+ LOG(ERROR) << "Failed to read smaps from file " << path;
+ maps_.clear();
+ }
+
+ return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+ if (get_wss_) {
+ LOG(WARNING) << "Trying to read process memory usage for " << pid_
+ << " using invalid object";
+ return usage_;
+ }
+
+ if (maps_.empty() && !ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to get memory usage for Process " << pid_;
+ }
+
+ return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+ if (!get_wss_) {
+ LOG(WARNING) << "Trying to read process working set for " << pid_
+ << " using invalid object";
+ return usage_;
+ }
+
+ if (maps_.empty() && !ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to get working set for Process " << pid_;
+ }
+
+ return usage_;
+}
+
+bool ProcMemInfo::ForEachVma(const VmaCallback& callback) {
+ std::string path = ::android::base::StringPrintf("/proc/%d/smaps", pid_);
+ return ForEachVmaFromFile(path, callback);
+}
+
+bool ProcMemInfo::SmapsOrRollup(MemUsage* stats) const {
+ std::string path = ::android::base::StringPrintf(
+ "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
+ return SmapsOrRollupFromFile(path, stats);
+}
+
+bool ProcMemInfo::SmapsOrRollupPss(uint64_t* pss) const {
+ std::string path = ::android::base::StringPrintf(
+ "/proc/%d/%s", pid_, IsSmapsRollupSupported(pid_) ? "smaps_rollup" : "smaps");
+ return SmapsOrRollupPssFromFile(path, pss);
+}
+
+const std::vector<uint16_t>& ProcMemInfo::SwapOffsets() {
+ if (get_wss_) {
+ LOG(WARNING) << "Trying to read process swap offsets for " << pid_
+ << " using invalid object";
+ return swap_offsets_;
+ }
+
+ if (maps_.empty() && !ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to get swap offsets for Process " << pid_;
+ }
+
+ return swap_offsets_;
+}
+
+bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
+ pagemap->clear();
+ std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+ ::android::base::unique_fd pagemap_fd(
+ TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (pagemap_fd == -1) {
+ PLOG(ERROR) << "Failed to open " << pagemap_file;
+ return false;
+ }
+
+ uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
+ pagemap->resize(nr_pages);
+
+ size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
+ off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
+ ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
+ if (bytes_read == -1) {
+ PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+ return false;
+ } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
+ LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
+ << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
+ return false;
+ }
+
+ return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
+ // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
+ // running for the lifetime of the system can recycle the objects and don't have to
+ // unnecessarily retain and update this object in memory (which can get significantly large).
+ // E.g. A program that only needs to reset the working set will never all ->Maps(), ->Usage().
+ // E.g. A program that is monitoring smaps_rollup, may never call ->maps(), Usage(), so it
+ // doesn't make sense for us to parse and retain unnecessary memory accounting stats by default.
+ if (!maps_.empty()) return true;
+
+ // parse and read /proc/<pid>/maps
+ std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
+ if (!::android::procinfo::ReadMapFile(
+ maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
+ const char* name) {
+ maps_.emplace_back(Vma(start, end, pgoff, flags, name));
+ })) {
+ LOG(ERROR) << "Failed to parse " << maps_file;
+ maps_.clear();
+ return false;
+ }
+
+ if (!get_usage_stats) {
+ return true;
+ }
+
+ std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+ ::android::base::unique_fd pagemap_fd(
+ TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (pagemap_fd < 0) {
+ PLOG(ERROR) << "Failed to open " << pagemap_file;
+ return false;
+ }
+
+ for (auto& vma : maps_) {
+ if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle)) {
+ LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
+ << vma.end << "]";
+ maps_.clear();
+ return false;
+ }
+ add_mem_usage(&usage_, vma.usage);
+ }
+
+ return true;
+}
+
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
+ PageAcct& pinfo = PageAcct::Instance();
+ if (get_wss && use_pageidle && !pinfo.InitPageAcct(true)) {
+ LOG(ERROR) << "Failed to init idle page accounting";
+ return false;
+ }
+
+ uint64_t pagesz = getpagesize();
+ size_t num_pages = (vma.end - vma.start) / pagesz;
+ size_t first_page = vma.start / pagesz;
+
+ std::vector<uint64_t> page_cache;
+ size_t cur_page_cache_index = 0;
+ size_t num_in_page_cache = 0;
+ size_t num_leftover_pages = num_pages;
+ for (size_t cur_page = first_page; cur_page < first_page + num_pages; ++cur_page) {
+ if (!get_wss) {
+ vma.usage.vss += pagesz;
+ }
+
+ // Cache page map data.
+ if (cur_page_cache_index == num_in_page_cache) {
+ static constexpr size_t kMaxPages = 2048;
+ num_leftover_pages -= num_in_page_cache;
+ if (num_leftover_pages > kMaxPages) {
+ num_in_page_cache = kMaxPages;
+ } else {
+ num_in_page_cache = num_leftover_pages;
+ }
+ page_cache.resize(num_in_page_cache);
+ size_t total_bytes = page_cache.size() * sizeof(uint64_t);
+ ssize_t bytes = pread64(pagemap_fd, page_cache.data(), total_bytes,
+ cur_page * sizeof(uint64_t));
+ if (bytes != total_bytes) {
+ if (bytes == -1) {
+ PLOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
+ << cur_page * sizeof(uint64_t);
+ } else {
+ LOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
+ << cur_page * sizeof(uint64_t) << std::dec << " read bytes " << bytes
+ << " expected bytes " << total_bytes;
+ }
+ return false;
+ }
+ cur_page_cache_index = 0;
+ }
+
+ uint64_t page_info = page_cache[cur_page_cache_index++];
+ if (!PAGE_PRESENT(page_info) && !PAGE_SWAPPED(page_info)) continue;
+
+ if (PAGE_SWAPPED(page_info)) {
+ vma.usage.swap += pagesz;
+ swap_offsets_.emplace_back(PAGE_SWAP_OFFSET(page_info));
+ continue;
+ }
+
+ uint64_t page_frame = PAGE_PFN(page_info);
+ uint64_t cur_page_flags;
+ if (!pinfo.PageFlags(page_frame, &cur_page_flags)) {
+ LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+ swap_offsets_.clear();
+ return false;
+ }
+
+ // skip unwanted pages from the count
+ if ((cur_page_flags & pgflags_mask_) != pgflags_) continue;
+
+ uint64_t cur_page_counts;
+ if (!pinfo.PageMapCount(page_frame, &cur_page_counts)) {
+ LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+ swap_offsets_.clear();
+ return false;
+ }
+
+ // Page was unmapped between the presence check at the beginning of the loop and here.
+ if (cur_page_counts == 0) {
+ continue;
+ }
+
+ bool is_dirty = !!(cur_page_flags & (1 << KPF_DIRTY));
+ bool is_private = (cur_page_counts == 1);
+ // Working set
+ if (get_wss) {
+ bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
+ : !!(cur_page_flags & (1 << KPF_REFERENCED));
+ if (!is_referenced) {
+ continue;
+ }
+ // This effectively makes vss = rss for the working set is requested.
+ // The libpagemap implementation returns vss > rss for
+ // working set, which doesn't make sense.
+ vma.usage.vss += pagesz;
+ }
+
+ vma.usage.rss += pagesz;
+ vma.usage.uss += is_private ? pagesz : 0;
+ vma.usage.pss += pagesz / cur_page_counts;
+ if (is_private) {
+ vma.usage.private_dirty += is_dirty ? pagesz : 0;
+ vma.usage.private_clean += is_dirty ? 0 : pagesz;
+ } else {
+ vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+ vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+ }
+ }
+ return true;
+}
+
+// Public APIs
+bool ForEachVmaFromFile(const std::string& path, const VmaCallback& callback) {
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ return false;
+ }
+
+ char* line = nullptr;
+ bool parsing_vma = false;
+ ssize_t line_len;
+ size_t line_alloc = 0;
+ Vma vma;
+ while ((line_len = getline(&line, &line_alloc, fp.get())) > 0) {
+ // Make sure the line buffer terminates like a C string for ReadMapFile
+ line[line_len] = '\0';
+
+ if (parsing_vma) {
+ if (parse_smaps_field(line, &vma.usage)) {
+ // This was a stats field
+ continue;
+ }
+
+ // Done collecting stats, make the call back
+ callback(vma);
+ parsing_vma = false;
+ }
+
+ vma.clear();
+ // If it has, we are looking for the vma stats
+ // 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
+ if (!::android::procinfo::ReadMapFileContent(
+ line, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t,
+ const char* name) {
+ vma.start = start;
+ vma.end = end;
+ vma.flags = flags;
+ vma.offset = pgoff;
+ vma.name = name;
+ })) {
+ LOG(ERROR) << "Failed to parse " << path;
+ return false;
+ }
+ parsing_vma = true;
+ }
+
+ // free getline() managed buffer
+ free(line);
+
+ if (parsing_vma) {
+ callback(vma);
+ }
+
+ return true;
+}
+
+enum smaps_rollup_support { UNTRIED, SUPPORTED, UNSUPPORTED };
+
+static std::atomic<smaps_rollup_support> g_rollup_support = UNTRIED;
+
+bool IsSmapsRollupSupported(pid_t pid) {
+ // Similar to OpenSmapsOrRollup checks from android_os_Debug.cpp, except
+ // the method only checks if rollup is supported and returns the status
+ // right away.
+ enum smaps_rollup_support rollup_support = g_rollup_support.load(std::memory_order_relaxed);
+ if (rollup_support != UNTRIED) {
+ return rollup_support == SUPPORTED;
+ }
+ std::string rollup_file = ::android::base::StringPrintf("/proc/%d/smaps_rollup", pid);
+ if (access(rollup_file.c_str(), F_OK | R_OK)) {
+ // No check for errno = ENOENT necessary here. The caller MUST fallback to
+ // using /proc/<pid>/smaps instead anyway.
+ g_rollup_support.store(UNSUPPORTED, std::memory_order_relaxed);
+ return false;
+ }
+
+ g_rollup_support.store(SUPPORTED, std::memory_order_relaxed);
+ LOG(INFO) << "Using smaps_rollup for pss collection";
+ return true;
+}
+
+bool SmapsOrRollupFromFile(const std::string& path, MemUsage* stats) {
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ return false;
+ }
+
+ char* line = nullptr;
+ size_t line_alloc = 0;
+ stats->clear();
+ while (getline(&line, &line_alloc, fp.get()) > 0) {
+ switch (line[0]) {
+ case 'P':
+ if (strncmp(line, "Pss:", 4) == 0) {
+ char* c = line + 4;
+ stats->pss += strtoull(c, nullptr, 10);
+ } else if (strncmp(line, "Private_Clean:", 14) == 0) {
+ char* c = line + 14;
+ uint64_t prcl = strtoull(c, nullptr, 10);
+ stats->private_clean += prcl;
+ stats->uss += prcl;
+ } else if (strncmp(line, "Private_Dirty:", 14) == 0) {
+ char* c = line + 14;
+ uint64_t prdi = strtoull(c, nullptr, 10);
+ stats->private_dirty += prdi;
+ stats->uss += prdi;
+ }
+ break;
+ case 'R':
+ if (strncmp(line, "Rss:", 4) == 0) {
+ char* c = line + 4;
+ stats->rss += strtoull(c, nullptr, 10);
+ }
+ break;
+ case 'S':
+ if (strncmp(line, "SwapPss:", 8) == 0) {
+ char* c = line + 8;
+ stats->swap_pss += strtoull(c, nullptr, 10);
+ }
+ break;
+ }
+ }
+
+ // free getline() managed buffer
+ free(line);
+ return true;
+}
+
+bool SmapsOrRollupPssFromFile(const std::string& path, uint64_t* pss) {
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ return false;
+ }
+ *pss = 0;
+ char* line = nullptr;
+ size_t line_alloc = 0;
+ while (getline(&line, &line_alloc, fp.get()) > 0) {
+ uint64_t v;
+ if (sscanf(line, "Pss: %" SCNu64 " kB", &v) == 1) {
+ *pss += v;
+ }
+ }
+
+ // free getline() managed buffer
+ free(line);
+ return true;
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
new file mode 100644
index 0000000..5cfa6c3
--- /dev/null
+++ b/libmeminfo/sysmeminfo.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
+ SysMemInfo::kMemTotal, SysMemInfo::kMemFree, SysMemInfo::kMemBuffers,
+ SysMemInfo::kMemCached, SysMemInfo::kMemShmem, SysMemInfo::kMemSlab,
+ SysMemInfo::kMemSReclaim, SysMemInfo::kMemSUnreclaim, SysMemInfo::kMemSwapTotal,
+ SysMemInfo::kMemSwapFree, SysMemInfo::kMemMapped, SysMemInfo::kMemVmallocUsed,
+ SysMemInfo::kMemPageTables, SysMemInfo::kMemKernelStack,
+};
+
+bool SysMemInfo::ReadMemInfo(const std::string& path) {
+ return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path,
+ [&](const std::string& tag, uint64_t val) { mem_in_kb_[tag] = val; });
+}
+
+bool SysMemInfo::ReadMemInfo(std::vector<uint64_t>* out, const std::string& path) {
+ return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, out, path);
+}
+
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, std::vector<uint64_t>* out,
+ const std::string& path) {
+ out->clear();
+ out->resize(tags.size());
+
+ return ReadMemInfo(tags, path, [&]([[maybe_unused]] const std::string& tag, uint64_t val) {
+ auto it = std::find(tags.begin(), tags.end(), tag);
+ if (it == tags.end()) {
+ LOG(ERROR) << "Tried to store invalid tag: " << tag;
+ return;
+ }
+ auto index = std::distance(tags.begin(), it);
+ // store the values in the same order as the tags
+ out->at(index) = val;
+ });
+}
+
+uint64_t SysMemInfo::ReadVmallocInfo() {
+ return ::android::meminfo::ReadVmallocInfo();
+}
+
+// TODO: Delete this function if it can't match up with the c-like implementation below.
+// Currently, this added about 50 % extra overhead on hikey.
+#if 0
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+ std::string buffer;
+ if (!::android::base::ReadFileToString(path, &buffer)) {
+ PLOG(ERROR) << "Failed to read : " << path;
+ return false;
+ }
+
+ uint32_t total_found = 0;
+ for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
+ for (auto& tag : tags) {
+ if (tag == std::string(s, s + tag.size())) {
+ s += tag.size();
+ while (isspace(*s)) s++;
+ auto num_start = s;
+ while (std::isdigit(*s)) s++;
+
+ std::string number(num_start, num_start + (s - num_start));
+ if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
+ LOG(ERROR) << "Failed to parse uint";
+ return false;
+ }
+ total_found++;
+ break;
+ }
+ }
+ while (s < buffer.end() && *s != '\n') s++;
+ if (s < buffer.end()) s++;
+ }
+
+ return true;
+}
+
+#else
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path,
+ std::function<void(const std::string&, uint64_t)> store_val) {
+ char buffer[4096];
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open file :" << path;
+ return false;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+ if (len < 0) {
+ return false;
+ }
+
+ buffer[len] = '\0';
+ char* p = buffer;
+ uint32_t found = 0;
+ uint32_t lineno = 0;
+ bool zram_tag_found = false;
+ while (*p && found < tags.size()) {
+ for (auto& tag : tags) {
+ // Special case for "Zram:" tag that android_os_Debug and friends look
+ // up along with the rest of the numbers from /proc/meminfo
+ if (!zram_tag_found && tag == "Zram:") {
+ store_val(tag, mem_zram_kb());
+ zram_tag_found = true;
+ found++;
+ continue;
+ }
+
+ if (strncmp(p, tag.c_str(), tag.size()) == 0) {
+ p += tag.size();
+ while (*p == ' ') p++;
+ char* endptr = nullptr;
+ uint64_t val = strtoull(p, &endptr, 10);
+ if (p == endptr) {
+ PLOG(ERROR) << "Failed to parse line:" << lineno + 1 << " in file: " << path;
+ return false;
+ }
+ store_val(tag, val);
+ p = endptr;
+ found++;
+ break;
+ }
+ }
+
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ lineno++;
+ }
+
+ return true;
+}
+#endif
+
+uint64_t SysMemInfo::mem_zram_kb(const std::string& zram_dev) {
+ uint64_t mem_zram_total = 0;
+ if (!zram_dev.empty()) {
+ if (!MemZramDevice(zram_dev, &mem_zram_total)) {
+ return 0;
+ }
+ return mem_zram_total / 1024;
+ }
+
+ constexpr uint32_t kMaxZramDevices = 256;
+ for (uint32_t i = 0; i < kMaxZramDevices; i++) {
+ std::string zram_dev = ::android::base::StringPrintf("/sys/block/zram%u/", i);
+ if (access(zram_dev.c_str(), F_OK)) {
+ // We assume zram devices appear in range 0-255 and appear always in sequence
+ // under /sys/block. So, stop looking for them once we find one is missing.
+ break;
+ }
+
+ uint64_t mem_zram_dev;
+ if (!MemZramDevice(zram_dev, &mem_zram_dev)) {
+ return 0;
+ }
+
+ mem_zram_total += mem_zram_dev;
+ }
+
+ return mem_zram_total / 1024;
+}
+
+bool SysMemInfo::MemZramDevice(const std::string& zram_dev, uint64_t* mem_zram_dev) {
+ std::string mmstat = ::android::base::StringPrintf("%s/%s", zram_dev.c_str(), "mm_stat");
+ auto mmstat_fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(mmstat.c_str(), "re"), fclose};
+ if (mmstat_fp != nullptr) {
+ // only if we do have mmstat, use it. Otherwise, fall through to trying out the old
+ // 'mem_used_total'
+ if (fscanf(mmstat_fp.get(), "%*" SCNu64 " %*" SCNu64 " %" SCNu64, mem_zram_dev) != 1) {
+ PLOG(ERROR) << "Malformed mm_stat file in: " << zram_dev;
+ return false;
+ }
+ return true;
+ }
+
+ std::string content;
+ if (::android::base::ReadFileToString(zram_dev + "mem_used_total", &content)) {
+ *mem_zram_dev = strtoull(content.c_str(), NULL, 10);
+ if (*mem_zram_dev == ULLONG_MAX) {
+ PLOG(ERROR) << "Malformed mem_used_total file for zram dev: " << zram_dev
+ << " content: " << content;
+ return false;
+ }
+
+ return true;
+ }
+
+ LOG(ERROR) << "Can't find memory status under: " << zram_dev;
+ return false;
+}
+
+// Public methods
+uint64_t ReadVmallocInfo(const std::string& path) {
+ uint64_t vmalloc_total = 0;
+ auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (fp == nullptr) {
+ return vmalloc_total;
+ }
+
+ char* line = nullptr;
+ size_t line_alloc = 0;
+ while (getline(&line, &line_alloc, fp.get()) > 0) {
+ // We are looking for lines like
+ //
+ // 0x0000000000000000-0x0000000000000000 12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
+ // 0x0000000000000000-0x0000000000000000 8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
+ //
+ // Notice that if the caller is coming from a module, the kernel prints and extra
+ // "[module_name]" after the address and the symbol of the call site. This means we can't
+ // use the old sscanf() method of getting the # of pages.
+ char* p_start = strstr(line, "pages=");
+ if (p_start == nullptr) {
+ // we didn't find anything
+ continue;
+ }
+
+ uint64_t nr_pages;
+ if (sscanf(p_start, "pages=%" SCNu64 "", &nr_pages) == 1) {
+ vmalloc_total += (nr_pages * getpagesize());
+ }
+ }
+
+ free(line);
+
+ return vmalloc_total;
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/testdata1/mm_stat b/libmeminfo/testdata1/mm_stat
new file mode 100644
index 0000000..32b325f
--- /dev/null
+++ b/libmeminfo/testdata1/mm_stat
@@ -0,0 +1 @@
+ 145674240 26801454 31236096 0 45772800 3042 1887 517
diff --git a/libmeminfo/testdata1/showmap_test.sh b/libmeminfo/testdata1/showmap_test.sh
new file mode 100755
index 0000000..8ee713e
--- /dev/null
+++ b/libmeminfo/testdata1/showmap_test.sh
@@ -0,0 +1,86 @@
+#! /system/bin/sh
+
+TESTDATA_PATH=/data/nativetest64/libmeminfo_test/testdata1
+SMAPS=$TESTDATA_PATH/smaps
+OUT1=$TMPDIR/1.txt
+OUT2=$TMPDIR/2.txt
+
+showmap -f $SMAPS > $OUT1
+showmap2 -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -f <smaps>";
+else
+ echo "pass: showmap -f <smaps>"
+fi
+
+showmap -q -f $SMAPS > $OUT1
+showmap2 -q -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -q -f <smaps>";
+else
+ echo "pass: showmap -q -f <smaps>"
+fi
+
+showmap -v -f $SMAPS > $OUT1
+showmap2 -v -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -v -f <smaps>";
+else
+ echo "pass: showmap -v -f <smaps>"
+fi
+
+showmap -a -f $SMAPS > $OUT1
+showmap2 -a -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -a -f <smaps>";
+else
+ echo "pass: showmap -a -f <smaps>"
+fi
+
+# Note that all tests from here down that have the option
+# '-a' added to the command are expected to fail as
+# 'showmap2' actually fixes the 64-bit address truncating
+# that was already happening with showmap
+showmap -a -t -f $SMAPS > $OUT1
+showmap2 -a -t -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -a -t -f <smaps>";
+else
+ echo "pass: showmap -a -t -f <smaps>"
+fi
+
+showmap -a -t -v -f $SMAPS > $OUT1
+showmap2 -a -t -v -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -a -t -v -f <smaps>";
+else
+ echo "pass: showmap -a -t -v -f <smaps>"
+fi
+
+# Note: This test again is expected to fail as the new
+# showmap fixes an issue with -t where the tool was only
+# showing maps with private dirty pages. The '-t' option was however
+# supposed to show all maps that have 'private' pages, clean or dirty.
+showmap -t -f $SMAPS > $OUT1
+showmap2 -t -f $SMAPS > $OUT2
+diff $OUT1 $OUT2 > /dev/null
+ret=$?
+if [[ $ret != 0 ]]; then
+ echo "fail: showmap -t -f <smaps>";
+else
+ echo "pass: showmap -t -f <smaps>"
+fi
+
+
diff --git a/libmeminfo/testdata1/smaps b/libmeminfo/testdata1/smaps
new file mode 100644
index 0000000..23aa2af
--- /dev/null
+++ b/libmeminfo/testdata1/smaps
@@ -0,0 +1,56297 @@
+12c00000-13440000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 8448 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2652 kB
+Pss: 2652 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 2652 kB
+Referenced: 2652 kB
+Anonymous: 2652 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2652 kB
+VmFlags: rd wr mr mw me ac
+13440000-13500000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13500000-13540000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13540000-137c0000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 2560 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+137c0000-13880000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 740 kB
+Pss: 740 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 740 kB
+Referenced: 740 kB
+Anonymous: 740 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 740 kB
+VmFlags: rd wr mr mw me ac
+13880000-13900000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 512 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13900000-13940000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13940000-13a40000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13a40000-13a80000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13a80000-13b40000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13b40000-13b80000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13b80000-13bc0000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13bc0000-13d80000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 1792 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1792 kB
+Pss: 1792 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 1792 kB
+Referenced: 1792 kB
+Anonymous: 1792 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1792 kB
+VmFlags: rd wr mr mw me ac
+13d80000-13dc0000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13dc0000-13e00000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13e00000-13e40000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13e40000-13e80000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 256 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 256 kB
+Referenced: 256 kB
+Anonymous: 256 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd wr mr mw me ac
+13e80000-13ec0000 ---p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+13ec0000-52c00000 rw-p 00000000 00:00 0 [anon:dalvik-main space (region space)]
+Name: [anon:dalvik-main space (region space)]
+Size: 1029376 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 768 kB
+Pss: 768 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 768 kB
+Referenced: 768 kB
+Anonymous: 768 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 768 kB
+VmFlags: rd wr mr mw me ac
+52c00000-54c00000 rw-p 00000000 00:00 0 [anon:dalvik-zygote-data-code-cache]
+Name: [anon:dalvik-zygote-data-code-cache]
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2048 kB
+Pss: 512 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 2048 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2048 kB
+Anonymous: 2048 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 512 kB
+VmFlags: rd wr mr mw me ac
+54c00000-56c00000 r-xp 00000000 00:00 0 [anon:dalvik-zygote-jit-code-cache]
+Name: [anon:dalvik-zygote-jit-code-cache]
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2048 kB
+Pss: 113 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 2048 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2048 kB
+Anonymous: 2048 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 113 kB
+VmFlags: rd ex mr mw me ac
+56c00000-56c05000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+56c05000-56c37000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 200 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 200 kB
+Pss: 11 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 200 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 200 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd wr mr mw me ac
+56c37000-56c45000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+56c45000-56c4e000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 36 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 36 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd wr mr mw me ac
+56c52000-56c5c000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 40 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 40 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr mr mw me ac
+56c5c000-56c61000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me ac
+56c63000-56c6e000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 44 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 44 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr mr mw me ac
+56c6e000-56c77000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+56c79000-56d05000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 560 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 560 kB
+Pss: 31 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 560 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 560 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 31 kB
+VmFlags: rd wr mr mw me ac
+56d05000-56d0e000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+56d10000-56d16000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd wr mr mw me ac
+56d1d000-56d21000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+56d21000-58d21000 rw-s 00000000 00:05 9572 /memfd:/jit-cache (deleted)
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr sh mr mw me ms
+58d21000-5ad21000 r-xs 02000000 00:05 9572 /memfd:/jit-cache (deleted)
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex sh mr mw me ms
+5ad21000-5ae6f000 rw-p 00000000 00:00 0 [anon:dalvik-/system/framework/oat/x86_64/services.art]
+Name: [anon:dalvik-/system/framework/oat/x86_64/services.art]
+Size: 1336 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1336 kB
+Pss: 1336 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 1336 kB
+Referenced: 1336 kB
+Anonymous: 1336 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1336 kB
+VmFlags: rd wr mr mw me ac
+5ae6f000-5aeb5000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 280 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 112 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 112 kB
+Referenced: 112 kB
+Anonymous: 112 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 112 kB
+VmFlags: rd wr mr mw me ac
+5aeb6000-5aebf000 rw-p 00000000 00:00 0 [anon:dalvik-large object space allocation]
+Name: [anon:dalvik-large object space allocation]
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+6fc4d000-6ff1e000 rw-p 00000000 fe:00 3184 /system/framework/x86_64/boot.art
+Size: 2884 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2884 kB
+Pss: 472 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 2552 kB
+Private_Clean: 0 kB
+Private_Dirty: 328 kB
+Referenced: 2728 kB
+Anonymous: 2880 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 472 kB
+VmFlags: rd wr mr mw me ac
+6ff1e000-70050000 rw-p 00000000 fe:00 3174 /system/framework/x86_64/boot-core-libart.art
+Size: 1224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1212 kB
+Pss: 234 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 1032 kB
+Private_Clean: 0 kB
+Private_Dirty: 176 kB
+Referenced: 1092 kB
+Anonymous: 1208 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 234 kB
+VmFlags: rd wr mr mw me ac
+70050000-70051000 rw-p 00000000 fe:00 3197 /system/framework/x86_64/boot-core-simple.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70051000-70091000 rw-p 00000000 fe:00 3168 /system/framework/x86_64/boot-conscrypt.art
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 89 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 172 kB
+Private_Clean: 0 kB
+Private_Dirty: 80 kB
+Referenced: 236 kB
+Anonymous: 252 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 89 kB
+VmFlags: rd wr mr mw me ac
+70091000-700ce000 rw-p 00000000 fe:00 3166 /system/framework/x86_64/boot-okhttp.art
+Size: 244 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 240 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 224 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 200 kB
+Anonymous: 240 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd wr mr mw me ac
+700ce000-70136000 rw-p 00000000 fe:00 3175 /system/framework/x86_64/boot-bouncycastle.art
+Size: 416 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 412 kB
+Pss: 22 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 412 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 236 kB
+Anonymous: 412 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 22 kB
+VmFlags: rd wr mr mw me ac
+70136000-7019d000 rw-p 00000000 fe:00 3167 /system/framework/x86_64/boot-apache-xml.art
+Size: 412 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 408 kB
+Pss: 22 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 408 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 276 kB
+Anonymous: 408 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 22 kB
+VmFlags: rd wr mr mw me ac
+7019d000-701ea000 rw-p 00000000 fe:00 3196 /system/framework/x86_64/boot-ext.art
+Size: 308 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 308 kB
+Pss: 20 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 300 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 256 kB
+Anonymous: 304 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me ac
+701ea000-70cdb000 rw-p 00000000 fe:00 3165 /system/framework/x86_64/boot-framework.art
+Size: 11204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 11188 kB
+Pss: 2200 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 9448 kB
+Private_Clean: 0 kB
+Private_Dirty: 1660 kB
+Referenced: 9892 kB
+Anonymous: 11108 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2200 kB
+VmFlags: rd wr mr mw me ac
+70cdb000-70df1000 rw-p 00000000 fe:00 3178 /system/framework/x86_64/boot-telephony-common.art
+Size: 1112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1112 kB
+Pss: 63 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 1108 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 524 kB
+Anonymous: 1108 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 63 kB
+VmFlags: rd wr mr mw me ac
+70df1000-70e02000 rw-p 00000000 fe:00 3198 /system/framework/x86_64/boot-voip-common.art
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 3 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 64 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 64 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd wr mr mw me ac
+70e02000-70e1e000 rw-p 00000000 fe:00 3176 /system/framework/x86_64/boot-ims-common.art
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 6 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 112 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 112 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd wr mr mw me ac
+70e1e000-70e20000 rw-p 00000000 fe:00 3201 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.art
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70e20000-70e26000 rw-p 00000000 fe:00 3170 /system/framework/x86_64/boot-android.test.base.impl.art
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd wr mr mw me ac
+70e26000-70f04000 r--p 00000000 fe:00 3199 /system/framework/x86_64/boot.oat
+Size: 888 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 856 kB
+Pss: 66 kB
+Shared_Clean: 856 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 856 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 66 kB
+VmFlags: rd mr mw me
+70f04000-71279000 r-xp 000de000 fe:00 3199 /system/framework/x86_64/boot.oat
+Size: 3540 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 3476 kB
+Pss: 308 kB
+Shared_Clean: 3412 kB
+Shared_Dirty: 0 kB
+Private_Clean: 64 kB
+Private_Dirty: 0 kB
+Referenced: 3476 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 308 kB
+VmFlags: rd ex mr mw me
+71279000-7127a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7127a000-71700000 r--s 00000000 fe:00 3396 /system/framework/boot.vdex
+Size: 4632 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 3592 kB
+Pss: 423 kB
+Shared_Clean: 3592 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 3592 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 423 kB
+VmFlags: rd mr me ms
+71700000-71701000 r--p 00453000 fe:00 3199 /system/framework/x86_64/boot.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71701000-71702000 rw-p 00454000 fe:00 3199 /system/framework/x86_64/boot.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71702000-7175d000 r--p 00000000 fe:00 3181 /system/framework/x86_64/boot-core-libart.oat
+Size: 364 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 356 kB
+Pss: 20 kB
+Shared_Clean: 356 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 356 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+7175d000-718b6000 r-xp 0005b000 fe:00 3181 /system/framework/x86_64/boot-core-libart.oat
+Size: 1380 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1360 kB
+Pss: 127 kB
+Shared_Clean: 1360 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 1360 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 127 kB
+VmFlags: rd ex mr mw me
+718b6000-718b7000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+718b7000-71bcb000 r--s 00000000 fe:00 3146 /system/framework/boot-core-libart.vdex
+Size: 3152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2228 kB
+Pss: 238 kB
+Shared_Clean: 2228 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2228 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 238 kB
+VmFlags: rd mr me ms
+71bcb000-71bcc000 r--p 001b4000 fe:00 3181 /system/framework/x86_64/boot-core-libart.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71bcc000-71bcd000 rw-p 001b5000 fe:00 3181 /system/framework/x86_64/boot-core-libart.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71bcd000-71bcf000 r--p 00000000 fe:00 3182 /system/framework/x86_64/boot-core-simple.oat
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+71bcf000-71bd0000 r-xp 00002000 fe:00 3182 /system/framework/x86_64/boot-core-simple.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+71bd0000-71bd1000 r--s 00000000 fe:00 3216 /system/framework/boot-core-simple.vdex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+71bd1000-71bd2000 r--p 00003000 fe:00 3182 /system/framework/x86_64/boot-core-simple.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71bd2000-71bd3000 rw-p 00004000 fe:00 3182 /system/framework/x86_64/boot-core-simple.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71bd3000-71be2000 r--p 00000000 fe:00 3205 /system/framework/x86_64/boot-conscrypt.oat
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 3 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+71be2000-71c12000 r-xp 0000f000 fe:00 3205 /system/framework/x86_64/boot-conscrypt.oat
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 184 kB
+Pss: 25 kB
+Shared_Clean: 184 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 184 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 25 kB
+VmFlags: rd ex mr mw me
+71c12000-71c13000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+71c13000-71c78000 r--s 00000000 fe:00 3142 /system/framework/boot-conscrypt.vdex
+Size: 404 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 404 kB
+Pss: 47 kB
+Shared_Clean: 404 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 404 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 47 kB
+VmFlags: rd mr me ms
+71c78000-71c79000 r--p 0003f000 fe:00 3205 /system/framework/x86_64/boot-conscrypt.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71c79000-71c7a000 rw-p 00040000 fe:00 3205 /system/framework/x86_64/boot-conscrypt.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71c7a000-71c8e000 r--p 00000000 fe:00 3183 /system/framework/x86_64/boot-okhttp.oat
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 13 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 13 kB
+VmFlags: rd mr mw me
+71c8e000-71cd1000 r-xp 00014000 fe:00 3183 /system/framework/x86_64/boot-okhttp.oat
+Size: 268 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 228 kB
+Pss: 134 kB
+Shared_Clean: 188 kB
+Shared_Dirty: 0 kB
+Private_Clean: 40 kB
+Private_Dirty: 0 kB
+Referenced: 228 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 134 kB
+VmFlags: rd ex mr mw me
+71cd1000-71cd2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71cd2000-71d32000 r--s 00000000 fe:00 3135 /system/framework/boot-okhttp.vdex
+Size: 384 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 312 kB
+Pss: 71 kB
+Shared_Clean: 288 kB
+Shared_Dirty: 0 kB
+Private_Clean: 24 kB
+Private_Dirty: 0 kB
+Referenced: 312 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 71 kB
+VmFlags: rd mr me ms
+71d32000-71d33000 r--p 00057000 fe:00 3183 /system/framework/x86_64/boot-okhttp.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71d33000-71d34000 rw-p 00058000 fe:00 3183 /system/framework/x86_64/boot-okhttp.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71d34000-71d48000 r--p 00000000 fe:00 3200 /system/framework/x86_64/boot-bouncycastle.oat
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+71d48000-71d84000 r-xp 00014000 fe:00 3200 /system/framework/x86_64/boot-bouncycastle.oat
+Size: 240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 188 kB
+Pss: 98 kB
+Shared_Clean: 180 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 188 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 98 kB
+VmFlags: rd ex mr mw me
+71d84000-71d85000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71d85000-71ec6000 r--s 00000000 fe:00 3145 /system/framework/boot-bouncycastle.vdex
+Size: 1284 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 444 kB
+Pss: 76 kB
+Shared_Clean: 444 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 444 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 76 kB
+VmFlags: rd mr me ms
+71ec6000-71ec7000 r--p 00050000 fe:00 3200 /system/framework/x86_64/boot-bouncycastle.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+71ec7000-71ec8000 rw-p 00051000 fe:00 3200 /system/framework/x86_64/boot-bouncycastle.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71ec8000-71ed2000 r--p 00000000 fe:00 3187 /system/framework/x86_64/boot-apache-xml.oat
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 2 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+71ed2000-71eec000 r-xp 0000a000 fe:00 3187 /system/framework/x86_64/boot-apache-xml.oat
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+71eec000-71eed000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+71eed000-72010000 r--s 00000000 fe:00 3212 /system/framework/boot-apache-xml.vdex
+Size: 1164 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 320 kB
+Pss: 35 kB
+Shared_Clean: 320 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 320 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 35 kB
+VmFlags: rd mr me ms
+72010000-72011000 r--p 00024000 fe:00 3187 /system/framework/x86_64/boot-apache-xml.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+72011000-72012000 rw-p 00025000 fe:00 3187 /system/framework/x86_64/boot-apache-xml.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+72012000-7201d000 r--p 00000000 fe:00 3188 /system/framework/x86_64/boot-ext.oat
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 2 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7201d000-72038000 r-xp 0000b000 fe:00 3188 /system/framework/x86_64/boot-ext.oat
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 108 kB
+Pss: 37 kB
+Shared_Clean: 108 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 108 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 37 kB
+VmFlags: rd ex mr mw me
+72038000-72039000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+72039000-72124000 r--s 00000000 fe:00 3221 /system/framework/boot-ext.vdex
+Size: 940 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 340 kB
+Pss: 48 kB
+Shared_Clean: 340 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 340 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 48 kB
+VmFlags: rd mr me ms
+72124000-72125000 r--p 00026000 fe:00 3188 /system/framework/x86_64/boot-ext.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+72125000-72126000 rw-p 00027000 fe:00 3188 /system/framework/x86_64/boot-ext.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+72126000-7240e000 r--p 00000000 fe:00 3189 /system/framework/x86_64/boot-framework.oat
+Size: 2976 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2944 kB
+Pss: 349 kB
+Shared_Clean: 2944 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2944 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 349 kB
+VmFlags: rd mr mw me
+7240e000-72ee4000 r-xp 002e8000 fe:00 3189 /system/framework/x86_64/boot-framework.oat
+Size: 11096 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 10444 kB
+Pss: 1678 kB
+Shared_Clean: 10252 kB
+Shared_Dirty: 0 kB
+Private_Clean: 192 kB
+Private_Dirty: 0 kB
+Referenced: 10444 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1678 kB
+VmFlags: rd ex mr mw me
+72ee4000-72ee6000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+72ee6000-7449a000 r--s 00000000 fe:00 3156 /system/framework/boot-framework.vdex
+Size: 22224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16932 kB
+Pss: 2196 kB
+Shared_Clean: 16872 kB
+Shared_Dirty: 0 kB
+Private_Clean: 60 kB
+Private_Dirty: 0 kB
+Referenced: 16932 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2196 kB
+VmFlags: rd mr me ms
+7449a000-7449b000 r--p 00dbe000 fe:00 3189 /system/framework/x86_64/boot-framework.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7449b000-7449c000 rw-p 00dbf000 fe:00 3189 /system/framework/x86_64/boot-framework.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7449c000-744f8000 r--p 00000000 fe:00 3202 /system/framework/x86_64/boot-telephony-common.oat
+Size: 368 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 5 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+744f8000-7463c000 r-xp 0005c000 fe:00 3202 /system/framework/x86_64/boot-telephony-common.oat
+Size: 1296 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7463c000-7463d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7463d000-74974000 r--s 00000000 fe:00 3209 /system/framework/boot-telephony-common.vdex
+Size: 3292 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 344 kB
+Pss: 21 kB
+Shared_Clean: 344 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 344 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr me ms
+74974000-74975000 r--p 001a0000 fe:00 3202 /system/framework/x86_64/boot-telephony-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+74975000-74976000 rw-p 001a1000 fe:00 3202 /system/framework/x86_64/boot-telephony-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+74976000-74978000 r--p 00000000 fe:00 3193 /system/framework/x86_64/boot-voip-common.oat
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+74978000-7497a000 r-xp 00002000 fe:00 3193 /system/framework/x86_64/boot-voip-common.oat
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7497a000-7497b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7497b000-749a1000 r--s 00000000 fe:00 3397 /system/framework/boot-voip-common.vdex
+Size: 152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 144 kB
+Pss: 14 kB
+Shared_Clean: 144 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 144 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd mr me ms
+749a1000-749a2000 r--p 00004000 fe:00 3193 /system/framework/x86_64/boot-voip-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+749a2000-749a3000 rw-p 00005000 fe:00 3193 /system/framework/x86_64/boot-voip-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+749a3000-749a9000 r--p 00000000 fe:00 3191 /system/framework/x86_64/boot-ims-common.oat
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+749a9000-749b7000 r-xp 00006000 fe:00 3191 /system/framework/x86_64/boot-ims-common.oat
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+749b7000-749b8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+749b8000-749db000 r--s 00000000 fe:00 3152 /system/framework/boot-ims-common.vdex
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 104 kB
+Pss: 6 kB
+Shared_Clean: 104 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 104 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr me ms
+749db000-749dc000 r--p 00014000 fe:00 3191 /system/framework/x86_64/boot-ims-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+749dc000-749dd000 rw-p 00015000 fe:00 3191 /system/framework/x86_64/boot-ims-common.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+749dd000-749df000 r--p 00000000 fe:00 3194 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+749df000-749e0000 r-xp 00002000 fe:00 3194 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+749e0000-749e1000 r--s 00000000 fe:00 3147 /system/framework/boot-framework-oahl-backward-compatibility.vdex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+749e1000-749e2000 r--p 00003000 fe:00 3194 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+749e2000-749e3000 rw-p 00004000 fe:00 3194 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+749e3000-749e5000 r--p 00000000 fe:00 3180 /system/framework/x86_64/boot-android.test.base.impl.oat
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+749e5000-749e6000 r-xp 00002000 fe:00 3180 /system/framework/x86_64/boot-android.test.base.impl.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+749e6000-749ee000 r--s 00000000 fe:00 3350 /system/framework/boot-android.test.base.impl.vdex
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+749ee000-749ef000 r--p 00003000 fe:00 3180 /system/framework/x86_64/boot-android.test.base.impl.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+749ef000-749f0000 rw-p 00004000 fe:00 3180 /system/framework/x86_64/boot-android.test.base.impl.oat
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+749f0000-74b3d000 rw-p 00000000 00:00 0 [anon:dalvik-zygote space]
+Name: [anon:dalvik-zygote space]
+Size: 1332 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1332 kB
+Pss: 497 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 884 kB
+Private_Clean: 0 kB
+Private_Dirty: 448 kB
+Referenced: 564 kB
+Anonymous: 1332 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 497 kB
+VmFlags: rd wr mr mw me ac
+74b3d000-74b3e000 rw-p 00000000 00:00 0 [anon:dalvik-non moving space]
+Name: [anon:dalvik-non moving space]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+74b3e000-74b71000 rw-p 00000000 00:00 0 [anon:dalvik-non moving space]
+Name: [anon:dalvik-non moving space]
+Size: 204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 204 kB
+Pss: 204 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 204 kB
+Referenced: 204 kB
+Anonymous: 204 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 204 kB
+VmFlags: rd wr mr mw me ac
+74b71000-781f1000 ---p 00000000 00:00 0 [anon:dalvik-non moving space]
+Name: [anon:dalvik-non moving space]
+Size: 55808 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+781f1000-789f0000 rw-p 00000000 00:00 0 [anon:dalvik-non moving space]
+Name: [anon:dalvik-non moving space]
+Size: 8188 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+ebad6000-ebad7000 ---p 00000000 00:00 0 [anon:dalvik-Sentinel fault page]
+Name: [anon:dalvik-Sentinel fault page]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+578de5770000-578de5773000 r--p 00000000 fe:00 408 /system/bin/app_process64
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me dw
+578de5773000-578de5777000 r-xp 00003000 fe:00 408 /system/bin/app_process64
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me dw
+578de5777000-578de5778000 r--p 00007000 fe:00 408 /system/bin/app_process64
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me dw ac
+578de5778000-578de577a000 rw-p 00000000 00:00 0
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700749747000-7007497f6000 rw-s 00000000 00:05 12329 /dev/ashmem/gralloc-1332.3 (deleted)
+Size: 700 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 656 kB
+Pss: 656 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 656 kB
+Referenced: 656 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 656 kB
+VmFlags: rd wr sh mr mw me ms
+7007497f6000-7007497f7000 ---s 000af000 00:05 12329 /dev/ashmem/gralloc-1332.3 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: sh mr mw me ms
+7007497f7000-7007498a6000 rw-s 00000000 00:05 12330 /dev/ashmem/gralloc-1332.4 (deleted)
+Size: 700 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 656 kB
+Pss: 656 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 656 kB
+Referenced: 656 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 656 kB
+VmFlags: rd wr sh mr mw me ms
+7007498a6000-7007498a7000 ---s 000af000 00:05 12330 /dev/ashmem/gralloc-1332.4 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: sh mr mw me ms
+7007498a7000-700749956000 rw-s 00000000 00:05 12331 /dev/ashmem/gralloc-1332.5 (deleted)
+Size: 700 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 656 kB
+Pss: 328 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 656 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 656 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 328 kB
+VmFlags: rd wr sh mr mw me ms
+700749956000-700749957000 ---s 000af000 00:05 12331 /dev/ashmem/gralloc-1332.5 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: sh mr mw me ms
+700749957000-70074995b000 r--p 00000000 fe:30 346 /vendor/lib64/hw/gralloc.vsoc.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 2 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+70074995b000-70074995e000 r-xp 00004000 fe:30 346 /vendor/lib64/hw/gralloc.vsoc.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+70074995e000-70074995f000 rw-p 00007000 fe:30 346 /vendor/lib64/hw/gralloc.vsoc.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70074995f000-700749960000 r--p 00008000 fe:30 346 /vendor/lib64/hw/gralloc.vsoc.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700749960000-700749961000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700749981000-70074998a000 r--p 00000000 fe:30 381 /vendor/lib64/vsoc_lib.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 3 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+70074998a000-700749992000 r-xp 00009000 fe:30 381 /vendor/lib64/vsoc_lib.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700749992000-700749993000 rw-p 00011000 fe:30 381 /vendor/lib64/vsoc_lib.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700749993000-700749994000 r--p 00012000 fe:30 381 /vendor/lib64/vsoc_lib.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700749994000-700749995000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007499f7000-70074a5f7000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 12288 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70074a909000-70074a90a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074a90a000-70074a90b000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074a90b000-70074aa0f000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70074aa0f000-70074ab0f000 rw-s 00000000 00:05 11658 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr sh mr mw me ms
+70074ab0f000-70074ac0f000 rw-s 00000000 00:05 11656 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr sh mr mw me ms
+70074ac0f000-70074c78d000 r--s 00bc3000 fe:00 3346 /system/framework/framework-res.apk
+Size: 28152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 19268 kB
+Pss: 933 kB
+Shared_Clean: 19268 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 19268 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 933 kB
+VmFlags: rd mr me ms
+70074c78d000-70074c78e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074c78e000-70074c78f000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074c78f000-70074c893000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074c893000-70074d293000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 10240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 536 kB
+Pss: 536 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 536 kB
+Referenced: 208 kB
+Anonymous: 536 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 536 kB
+VmFlags: rd wr mr mw me ac
+70074d293000-70074d294000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074d294000-70074d295000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074d295000-70074d399000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074d399000-70074db99000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 8192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70074db99000-70074db9a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074db9a000-70074dc93000 rw-p 00000000 00:00 0
+Size: 996 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me nr
+70074dc93000-70074dc94000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074dc94000-70074dd8d000 rw-p 00000000 00:00 0
+Size: 996 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074dd8d000-70074ee0d000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 16896 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 15272 kB
+Pss: 15272 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 15272 kB
+Referenced: 11156 kB
+Anonymous: 15272 kB
+AnonHugePages: 6144 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15272 kB
+VmFlags: rd wr mr mw me ac
+70074ee0d000-70074ee0e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074ee0e000-70074ee0f000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074ee0f000-70074ef13000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074ef13000-70074f493000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 5632 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 5348 kB
+Pss: 5348 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 5348 kB
+Referenced: 3364 kB
+Anonymous: 5348 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5348 kB
+VmFlags: rd wr mr mw me ac
+70074f493000-70074f8a0000 r--s 001a6000 fe:00 1821 /system/priv-app/Telecom/Telecom.apk
+Size: 4148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr me ms
+70074f8e1000-70074f9e1000 rw-s 00000000 00:05 11617 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr sh mr mw me ms
+70074f9e1000-70074fae1000 rw-s 00000000 00:05 11565 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr sh mr mw me ms
+70074fae1000-70074fae2000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074fae2000-70074fae3000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074fae3000-70074fbe7000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074fbe7000-70074fca2000 r--s 0287a000 fe:00 3346 /system/framework/framework-res.apk
+Size: 748 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 748 kB
+Pss: 160 kB
+Shared_Clean: 748 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 748 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 160 kB
+VmFlags: rd mr me ms
+70074fca2000-70074fca4000 r--p 00000000 fe:00 2040 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+70074fca4000-70074fca7000 r-xp 00002000 fe:00 2040 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me
+70074fca7000-70074fca8000 r--p 00005000 fe:00 2040 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+70074fca8000-70074fca9000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70074fca9000-70074fcaa000 r--s 00000000 fe:00 2039 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.vdex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr me ms
+70074fcaa000-70074fcab000 r--p 00006000 fe:00 2040 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+70074fcab000-70074fcac000 rw-p 00007000 fe:00 2040 /system/priv-app/FusedLocation/oat/x86_64/FusedLocation.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70074fcef000-70074fcf2000 r--p 00000000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+70074fcf2000-70074fcf6000 r-xp 00003000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd ex mr mw me
+70074fcf6000-70074fcf7000 r--p 00007000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+70074fcf7000-70074fcf8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70074fcf8000-70074fcfd000 r--s 00000000 fe:00 3249 /system/framework/oat/x86_64/com.android.location.provider.impl.vdex
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 10 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr me ms
+70074fcfd000-70074fcfe000 r--p 00008000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+70074fcfe000-70074fcff000 rw-p 00009000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70074fd15000-70074fe15000 rw-s 00000000 00:05 11532 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr sh mr mw me ms
+70074fe15000-70074fe16000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074fe16000-70074fe17000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70074fe17000-70074ff1b000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70074ff1b000-70074ffa5000 r--p 00000000 fe:00 1819 /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size: 552 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 72 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 72 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 72 kB
+VmFlags: rd mr mw me
+70074ffa5000-7007501f2000 r-xp 0008a000 fe:00 1819 /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size: 2356 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1784 kB
+Pss: 1784 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 1784 kB
+Private_Dirty: 0 kB
+Referenced: 1784 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1784 kB
+VmFlags: rd ex mr mw me
+7007501f2000-7007501f4000 r--p 002d7000 fe:00 1819 /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007501f4000-7007501f9000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me ac
+7007501f9000-7007501fe000 r--s 00000000 fe:00 1820 /system/priv-app/Telecom/oat/x86_64/Telecom.vdex
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 20 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr me ms
+7007501fe000-7007501ff000 r--p 002d9000 fe:00 1819 /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007501ff000-700750200000 rw-p 002da000 fe:00 1819 /system/priv-app/Telecom/oat/x86_64/Telecom.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700750296000-700750297000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700750297000-700750298000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700750298000-70075039c000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075039c000-70075039d000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075039d000-70075039e000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075039e000-700750496000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700750496000-70075056f000 r--s 003f5000 fe:00 1805 /system/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk
+Size: 868 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 384 kB
+Pss: 192 kB
+Shared_Clean: 384 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 384 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 192 kB
+VmFlags: rd mr me ms
+7007509c2000-700750dc2000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 4096 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1980 kB
+Pss: 1980 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 1980 kB
+Referenced: 176 kB
+Anonymous: 1980 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1980 kB
+VmFlags: rd wr mr mw me ac
+70075185d000-70075185e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075185e000-70075185f000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075185f000-700751957000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700751adb000-700751adc000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751adc000-700751add000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751add000-700751be1000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd wr mr mw me nr
+700751d30000-700751d31000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751d31000-700751d32000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751d32000-700751e2a000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700751e50000-700751e51000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751e51000-700751e52000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751e52000-700751f56000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700751f56000-700751f57000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751f57000-700751f58000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700751f58000-70075205c000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075205c000-70075205d000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075205d000-70075205e000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075205e000-700752162000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700752162000-700752163000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752163000-700752164000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752164000-70075225c000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075225c000-70075225d000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075225d000-70075225e000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075225e000-700752362000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700752362000-700752363000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752363000-700752364000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752364000-700752468000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700752474000-700752475000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752475000-700752476000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752476000-70075256e000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075256e000-70075266e000 rw-s 00000000 00:05 11287 /dev/ashmem/MemoryHeapBase (deleted)
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr sh mr mw me ms
+70075266e000-70075266f000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075266f000-700752670000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752670000-700752774000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700752780000-700752781000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752781000-700752782000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752782000-70075287a000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70075287a000-70075287b000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075287b000-70075287c000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075287c000-700752980000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700752980000-700752981000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752981000-700752982000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752982000-700752a7a000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700752a7a000-700752a7b000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752a7b000-700752a7c000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752a7c000-700752b74000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700752b74000-700752b75000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752b75000-700752b76000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752b76000-700752c6e000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700752cb5000-700752cb8000 r--p 00000000 fe:00 1482 /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+700752cb8000-700752cb9000 r-xp 00003000 fe:00 1482 /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+700752cb9000-700752cba000 rw-p 00004000 fe:00 1482 /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700752cba000-700752cbb000 r--p 00005000 fe:00 1482 /system/lib64/vndk-sp-Q/hw/android.hidl.memory@1.0-impl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700752cbb000-700752cbc000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700752cf5000-700752cf6000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752cf6000-700752cf7000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752cf7000-700752dfb000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700752dfb000-700752dfc000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752dfc000-700752dfd000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752dfd000-700752ef5000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700752ef5000-700752ef6000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752ef6000-700752ef7000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752ef7000-700752fef000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700752fef000-700752ff0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752ff0000-700752ff1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700752ff1000-7007530e9000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700753145000-700753148000 r--p 00000000 fe:30 382 /vendor/lib64/libcuttlefish_fs.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700753148000-70075314d000 r-xp 00003000 fe:30 382 /vendor/lib64/libcuttlefish_fs.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+70075314d000-70075314e000 rw-p 00008000 fe:30 382 /vendor/lib64/libcuttlefish_fs.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075314e000-70075314f000 r--p 00009000 fe:30 382 /vendor/lib64/libcuttlefish_fs.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+70075319d000-70075319e000 r--p 00000000 fe:30 408 /vendor/lib64/cuttlefish_auto_resources.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+70075319e000-70075319f000 r-xp 00001000 fe:30 408 /vendor/lib64/cuttlefish_auto_resources.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+70075319f000-7007531a0000 rw-p 00002000 fe:30 408 /vendor/lib64/cuttlefish_auto_resources.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007531a0000-7007531a1000 r--p 00003000 fe:30 408 /vendor/lib64/cuttlefish_auto_resources.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007531e3000-7007531e4000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007531e4000-7007531e5000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007531e5000-7007532e9000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+7007532e9000-700753669000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 3584 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 3060 kB
+Pss: 3060 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 3060 kB
+Referenced: 1816 kB
+Anonymous: 3060 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3060 kB
+VmFlags: rd wr mr mw me ac
+700753669000-70075366a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075366a000-70075366b000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075366b000-70075376f000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075376f000-700753770000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753770000-700753771000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753771000-700753875000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700753875000-700753876000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753876000-700753877000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753877000-70075397b000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70075397b000-70075397c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075397c000-70075397d000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075397d000-700753a81000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700753a81000-700753a82000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753a82000-700753a83000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753a83000-700753b87000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700753b87000-700753b88000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753b88000-700753b89000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753b89000-700753c8d000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700753c8d000-700753c8e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753c8e000-700753c8f000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753c8f000-700753d93000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700753d93000-700753d94000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753d94000-700753d95000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753d95000-700753e99000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700753e99000-700753e9a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753e9a000-700753e9b000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753e9b000-700753f9f000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd wr mr mw me nr
+700753f9f000-700753fa0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753fa0000-700753fa1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700753fa1000-700754099000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700754099000-70075409a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075409a000-70075409b000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075409b000-70075419f000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075419f000-7007541a0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007541a0000-7007541a1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007541a1000-7007542a5000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007542a5000-7007542a6000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007542a6000-7007542a7000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007542a7000-7007543ab000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007543ab000-7007543ac000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007543ac000-7007543ad000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007543ad000-7007544b1000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007544b1000-7007544b2000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007544b2000-7007544b3000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007544b3000-7007545b7000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007545b7000-7007545b8000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007545b8000-7007545b9000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007545b9000-7007546bd000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007546bd000-7007546be000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007546be000-7007546bf000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007546bf000-7007547c3000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+7007547c3000-7007547c4000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007547c4000-7007547c5000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007547c5000-7007548c9000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007548c9000-7007548ca000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007548ca000-7007548cb000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007548cb000-7007549cf000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007549cf000-7007549d0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007549d0000-7007549d1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007549d1000-700754ad5000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700754ad5000-700754ad6000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ad6000-700754ad7000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ad7000-700754bdb000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700754bdb000-700754bdc000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754bdc000-700754bdd000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754bdd000-700754ce1000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700754ce1000-700754ce2000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ce2000-700754ce3000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ce3000-700754de7000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700754de7000-700754de8000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754de8000-700754de9000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754de9000-700754eed000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700754eed000-700754eee000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754eee000-700754eef000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754eef000-700754ff3000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700754ff3000-700754ff4000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ff4000-700754ff5000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700754ff5000-7007550f9000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007550f9000-7007550fa000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007550fa000-7007550fb000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007550fb000-7007551ff000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007551ff000-700755200000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755200000-700755201000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755201000-700755305000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700755305000-700755306000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755306000-700755307000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755307000-70075540b000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075540b000-70075540c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075540c000-70075540d000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075540d000-700755511000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700755511000-700755512000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755512000-700755513000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755513000-70075560b000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me nr
+70075560b000-70075560c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075560c000-70075560d000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075560d000-700755705000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700755705000-700755706000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755706000-700755707000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755707000-70075580b000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70075580b000-70075580c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075580c000-70075580d000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075580d000-700755911000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700755911000-700755912000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755912000-700755913000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755913000-700755a17000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700755a17000-700755a2d000 r--p 00000000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 88 kB
+Pss: 88 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 88 kB
+Private_Dirty: 0 kB
+Referenced: 88 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 88 kB
+VmFlags: rd mr mw me
+700755a2d000-700755a6e000 r-xp 00016000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 260 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 260 kB
+Pss: 260 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 260 kB
+Private_Dirty: 0 kB
+Referenced: 260 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 260 kB
+VmFlags: rd ex mr mw me
+700755a6e000-700755a6f000 r--p 00057000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700755a6f000-700755a70000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700755a70000-700755a71000 r--s 00000000 fe:00 1946 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.vdex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr me ms
+700755a71000-700755a72000 r--p 00058000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+700755a72000-700755a73000 rw-p 00059000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700755aae000-700755aaf000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755aaf000-700755ab0000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755ab0000-700755bb4000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700755bb4000-700755bb5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755bb5000-700755bb6000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755bb6000-700755cba000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700755cba000-700755cbb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755cbb000-700755cbc000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755cbc000-700755db4000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700755db4000-700755db5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755db5000-700755db6000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700755db6000-700755eae000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700755f18000-700755f28000 r--p 00000000 fe:00 1483 /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 32 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me
+700755f28000-700755f3c000 r-xp 00010000 fe:00 1483 /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 40 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd ex mr mw me
+700755f3c000-700755f3d000 rw-p 00024000 fe:00 1483 /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700755f3d000-700755f40000 r--p 00025000 fe:00 1483 /system/lib64/vndk-sp-Q/android.hidl.memory@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+700755f71000-7007560ba000 r--p 00000000 fe:00 1821 /system/priv-app/Telecom/Telecom.apk
+Size: 1316 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 128 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd mr mw me ac
+7007560ba000-7007560bb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007560bb000-7007560bc000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007560bc000-7007561c0000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007561c0000-7007561c1000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007561c1000-7007561c2000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007561c2000-7007562c6000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007562c6000-7007562c7000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007562c7000-7007562c8000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007562c8000-7007563cc000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007563d8000-7007563d9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007563d9000-7007563da000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007563da000-7007564d2000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 72 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 72 kB
+Referenced: 72 kB
+Anonymous: 72 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 72 kB
+VmFlags: rd wr mr mw me nr
+7007564d2000-7007564d3000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007564d3000-7007564d4000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007564d4000-7007565d8000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007565d8000-7007565d9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007565d9000-7007565da000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007565da000-7007566de000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007566de000-7007566df000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007566df000-7007566e0000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007566e0000-7007567e4000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+7007567e4000-7007567e5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007567e5000-7007567e6000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007567e6000-7007568de000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007568de000-7007568df000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007568df000-7007568e0000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007568e0000-7007569e4000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007569e4000-7007569e5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007569e5000-7007569e6000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007569e6000-700756aea000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700756aea000-700756aeb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756aeb000-700756aec000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756aec000-700756bf0000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 76 kB
+Pss: 76 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 76 kB
+Referenced: 76 kB
+Anonymous: 76 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 76 kB
+VmFlags: rd wr mr mw me nr
+700756bf0000-700756bf1000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756bf1000-700756bf2000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756bf2000-700756cf6000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700756cf6000-700756cf7000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756cf7000-700756cf8000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756cf8000-700756dfc000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700756dfc000-700756dfd000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756dfd000-700756dfe000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700756dfe000-700756f02000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700756f02000-700757000000 r--p 00000000 00:13 6792 /dev/hwbinder
+Size: 1016 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr me dc nr mm
+700757000000-700757400000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 4096 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2048 kB
+Pss: 2048 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 2048 kB
+Referenced: 2048 kB
+Anonymous: 2048 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2048 kB
+VmFlags: rd wr mr mw me ac
+700757442000-700757443000 r--p 00000000 fe:00 1554 /system/lib64/libwifi-service.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+700757443000-700757444000 r-xp 00001000 fe:00 1554 /system/lib64/libwifi-service.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+700757444000-700757445000 rw-p 00002000 fe:00 1554 /system/lib64/libwifi-service.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700757445000-700757446000 r--p 00003000 fe:00 1554 /system/lib64/libwifi-service.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+70075749f000-7007574a0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007574a0000-7007574a1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007574a1000-7007575a5000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007575a5000-7007578a5000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 3072 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2728 kB
+Pss: 2728 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 2728 kB
+Referenced: 1524 kB
+Anonymous: 2728 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2728 kB
+VmFlags: rd wr mr mw me ac
+7007578a5000-7007578a6000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007578a6000-7007578a7000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007578a7000-7007579ab000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+7007579ab000-7007579ac000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007579ac000-7007579ad000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+7007579ad000-700757ab1000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+700757ab1000-700757ab2000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757ab2000-700757ab3000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757ab3000-700757bb7000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me nr
+700757bb7000-700757bb8000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757bb8000-700757bb9000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757bb9000-700757cbd000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 24 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 24 kB
+Referenced: 24 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd wr mr mw me nr
+700757cbd000-700757cbe000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757cbe000-700757cbf000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757cbf000-700757dc3000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd wr mr mw me nr
+700757dcf000-700757dd0000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757dd0000-700757dd1000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700757dd1000-700757ec9000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+700757ec9000-700757ed4000 r--p 00000000 fe:00 1737 /system/lib64/android.hardware.vibrator@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700757ed4000-700757ee0000 r-xp 0000b000 fe:00 1737 /system/lib64/android.hardware.vibrator@1.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 48 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 48 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 48 kB
+VmFlags: rd ex mr mw me
+700757ee0000-700757ee1000 rw-p 00017000 fe:00 1737 /system/lib64/android.hardware.vibrator@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700757ee1000-700757ee3000 r--p 00018000 fe:00 1737 /system/lib64/android.hardware.vibrator@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700757f0c000-700757f3c000 r--p 00000000 fe:00 1129 /system/lib64/libinputflinger.so
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 112 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 112 kB
+Private_Dirty: 0 kB
+Referenced: 112 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 112 kB
+VmFlags: rd mr mw me
+700757f3c000-700757f6f000 r-xp 00030000 fe:00 1129 /system/lib64/libinputflinger.so
+Size: 204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 200 kB
+Pss: 200 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 200 kB
+Private_Dirty: 0 kB
+Referenced: 200 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 200 kB
+VmFlags: rd ex mr mw me
+700757f6f000-700757f70000 rw-p 00063000 fe:00 1129 /system/lib64/libinputflinger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700757f70000-700757f74000 r--p 00064000 fe:00 1129 /system/lib64/libinputflinger.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me ac
+700757f74000-700757f75000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700757f82000-700757faa000 r--p 00000000 fe:00 1625 /system/lib64/android.hardware.gnss@1.1.so
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 68 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 68 kB
+Private_Dirty: 0 kB
+Referenced: 68 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 68 kB
+VmFlags: rd mr mw me
+700757faa000-700757fe1000 r-xp 00028000 fe:00 1625 /system/lib64/android.hardware.gnss@1.1.so
+Size: 220 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 216 kB
+Pss: 216 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 216 kB
+Private_Dirty: 0 kB
+Referenced: 216 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 216 kB
+VmFlags: rd ex mr mw me
+700757fe1000-700757fe2000 rw-p 0005f000 fe:00 1625 /system/lib64/android.hardware.gnss@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700757fe2000-700757fea000 r--p 00060000 fe:00 1625 /system/lib64/android.hardware.gnss@1.1.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 32 kB
+Referenced: 32 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me ac
+700758023000-70075802d000 r--p 00000000 fe:00 1510 /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 20 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+70075802d000-700758036000 r-xp 0000a000 fe:00 1510 /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 36 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 36 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 36 kB
+VmFlags: rd ex mr mw me
+700758036000-700758037000 rw-p 00013000 fe:00 1510 /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758037000-700758039000 r--p 00014000 fe:00 1510 /system/lib64/android.hardware.tetheroffload.config@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758043000-700758053000 r--p 00000000 fe:00 1259 /system/lib64/libkeymaster4support.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700758053000-700758069000 r-xp 00010000 fe:00 1259 /system/lib64/libkeymaster4support.so
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700758069000-70075806a000 rw-p 00026000 fe:00 1259 /system/lib64/libkeymaster4support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075806a000-70075806c000 r--p 00027000 fe:00 1259 /system/lib64/libkeymaster4support.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758089000-700758099000 r--p 00000000 fe:00 1175 /system/lib64/android.hardware.tv.input@1.0.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+700758099000-7007580ab000 r-xp 00010000 fe:00 1175 /system/lib64/android.hardware.tv.input@1.0.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 72 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 72 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 72 kB
+VmFlags: rd ex mr mw me
+7007580ab000-7007580ac000 rw-p 00022000 fe:00 1175 /system/lib64/android.hardware.tv.input@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007580ac000-7007580af000 r--p 00023000 fe:00 1175 /system/lib64/android.hardware.tv.input@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+7007580d7000-7007580d8000 r--p 00000000 fe:00 1648 /system/lib64/android.hardware.audio.common@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007580d8000-7007580d9000 r-xp 00001000 fe:00 1648 /system/lib64/android.hardware.audio.common@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007580d9000-7007580da000 rw-p 00002000 fe:00 1648 /system/lib64/android.hardware.audio.common@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007580da000-7007580db000 r--p 00003000 fe:00 1648 /system/lib64/android.hardware.audio.common@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700758110000-700758122000 r--p 00000000 fe:00 1680 /system/lib64/android.hardware.tv.cec@1.0.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+700758122000-70075813c000 r-xp 00012000 fe:00 1680 /system/lib64/android.hardware.tv.cec@1.0.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 104 kB
+Pss: 104 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 104 kB
+Private_Dirty: 0 kB
+Referenced: 104 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 104 kB
+VmFlags: rd ex mr mw me
+70075813c000-70075813d000 rw-p 0002c000 fe:00 1680 /system/lib64/android.hardware.tv.cec@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075813d000-700758140000 r--p 0002d000 fe:00 1680 /system/lib64/android.hardware.tv.cec@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+700758153000-70075815e000 r--p 00000000 fe:00 1725 /system/lib64/android.hardware.thermal@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 14 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd mr mw me
+70075815e000-700758169000 r-xp 0000b000 fe:00 1725 /system/lib64/android.hardware.thermal@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 14 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd ex mr mw me
+700758169000-70075816a000 rw-p 00016000 fe:00 1725 /system/lib64/android.hardware.thermal@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075816a000-70075816c000 r--p 00017000 fe:00 1725 /system/lib64/android.hardware.thermal@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758198000-7007581a3000 r--p 00000000 fe:00 1171 /system/lib64/android.hardware.power@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 14 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd mr mw me
+7007581a3000-7007581ae000 r-xp 0000b000 fe:00 1171 /system/lib64/android.hardware.power@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 14 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd ex mr mw me
+7007581ae000-7007581af000 rw-p 00016000 fe:00 1171 /system/lib64/android.hardware.power@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007581af000-7007581b1000 r--p 00017000 fe:00 1171 /system/lib64/android.hardware.power@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007581c1000-7007581e1000 r--s 00000000 00:13 6677 /dev/__properties__/u:object_r:boottime_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr me ms
+7007581e1000-7007581ec000 r--p 00000000 fe:00 1568 /system/lib64/android.hardware.vibrator@1.1.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+7007581ec000-7007581f7000 r-xp 0000b000 fe:00 1568 /system/lib64/android.hardware.vibrator@1.1.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 44 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 44 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 44 kB
+VmFlags: rd ex mr mw me
+7007581f7000-7007581f8000 rw-p 00016000 fe:00 1568 /system/lib64/android.hardware.vibrator@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007581f8000-7007581fa000 r--p 00017000 fe:00 1568 /system/lib64/android.hardware.vibrator@1.1.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758202000-700758212000 r--p 00000000 fe:00 1534 /system/lib64/android.hardware.keymaster@3.0.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700758212000-70075822d000 r-xp 00010000 fe:00 1534 /system/lib64/android.hardware.keymaster@3.0.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 108 kB
+Pss: 27 kB
+Shared_Clean: 108 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 108 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 27 kB
+VmFlags: rd ex mr mw me
+70075822d000-70075822e000 rw-p 0002b000 fe:00 1534 /system/lib64/android.hardware.keymaster@3.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075822e000-700758231000 r--p 0002c000 fe:00 1534 /system/lib64/android.hardware.keymaster@3.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+700758243000-70075824d000 r--p 00000000 fe:00 1257 /system/lib64/android.hardware.vr@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 20 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+70075824d000-700758256000 r-xp 0000a000 fe:00 1257 /system/lib64/android.hardware.vr@1.0.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 36 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 36 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 36 kB
+VmFlags: rd ex mr mw me
+700758256000-700758257000 rw-p 00013000 fe:00 1257 /system/lib64/android.hardware.vr@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758257000-700758259000 r--p 00014000 fe:00 1257 /system/lib64/android.hardware.vr@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758267000-700758287000 r--s 00000000 00:13 6710 /dev/__properties__/u:object_r:firstboot_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700758287000-700758299000 r--p 00000000 fe:00 1586 /system/lib64/android.hardware.keymaster@4.0.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700758299000-7007582ba000 r-xp 00012000 fe:00 1586 /system/lib64/android.hardware.keymaster@4.0.so
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 104 kB
+Pss: 26 kB
+Shared_Clean: 104 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 104 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 26 kB
+VmFlags: rd ex mr mw me
+7007582ba000-7007582bb000 rw-p 00033000 fe:00 1586 /system/lib64/android.hardware.keymaster@4.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007582bb000-7007582be000 r--p 00034000 fe:00 1586 /system/lib64/android.hardware.keymaster@4.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+7007582ce000-7007582ea000 r--p 00000000 fe:00 1729 /system/lib64/android.frameworks.sensorservice@1.0.so
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007582ea000-70075830d000 r-xp 0001c000 fe:00 1729 /system/lib64/android.frameworks.sensorservice@1.0.so
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 140 kB
+Pss: 140 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 140 kB
+Private_Dirty: 0 kB
+Referenced: 140 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 140 kB
+VmFlags: rd ex mr mw me
+70075830d000-70075830e000 rw-p 0003f000 fe:00 1729 /system/lib64/android.frameworks.sensorservice@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075830e000-700758313000 r--p 00040000 fe:00 1729 /system/lib64/android.frameworks.sensorservice@1.0.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me ac
+700758367000-7007583d9000 r--p 00000000 fe:00 1138 /system/lib64/android.hardware.gnss@1.0.so
+Size: 456 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 64 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 64 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 64 kB
+VmFlags: rd mr mw me
+7007583d9000-700758494000 r-xp 00072000 fe:00 1138 /system/lib64/android.hardware.gnss@1.0.so
+Size: 748 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 724 kB
+Pss: 724 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 724 kB
+Private_Dirty: 0 kB
+Referenced: 724 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 724 kB
+VmFlags: rd ex mr mw me
+700758494000-700758495000 rw-p 0012d000 fe:00 1138 /system/lib64/android.hardware.gnss@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758495000-7007584ac000 r--p 0012e000 fe:00 1138 /system/lib64/android.hardware.gnss@1.0.so
+Size: 92 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 92 kB
+Pss: 92 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 92 kB
+Referenced: 92 kB
+Anonymous: 92 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 92 kB
+VmFlags: rd mr mw me ac
+7007584f4000-7007584f7000 r--p 00000000 fe:00 1550 /system/lib64/libtinyalsa.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+7007584f7000-7007584fb000 r-xp 00003000 fe:00 1550 /system/lib64/libtinyalsa.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007584fb000-7007584fc000 rw-p 00007000 fe:00 1550 /system/lib64/libtinyalsa.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007584fc000-7007584fd000 r--p 00008000 fe:00 1550 /system/lib64/libtinyalsa.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700758501000-70075850d000 r--p 00000000 fe:00 1197 /system/lib64/android.hardware.power@1.1.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 15 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd mr mw me
+70075850d000-700758519000 r-xp 0000c000 fe:00 1197 /system/lib64/android.hardware.power@1.1.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 15 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd ex mr mw me
+700758519000-70075851a000 rw-p 00018000 fe:00 1197 /system/lib64/android.hardware.power@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075851a000-70075851c000 r--p 00019000 fe:00 1197 /system/lib64/android.hardware.power@1.1.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758521000-700758541000 r--s 00000000 00:13 6676 /dev/__properties__/u:object_r:bootloader_boot_reason_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700758541000-700758552000 r--p 00000000 fe:00 1517 /system/lib64/libsensorservice.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 20 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+700758552000-700758572000 r-xp 00011000 fe:00 1517 /system/lib64/libsensorservice.so
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 128 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd ex mr mw me
+700758572000-700758573000 rw-p 00031000 fe:00 1517 /system/lib64/libsensorservice.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758573000-700758577000 r--p 00032000 fe:00 1517 /system/lib64/libsensorservice.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me ac
+700758577000-700758578000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758586000-700758591000 r--p 00000000 fe:00 1649 /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700758591000-70075859b000 r-xp 0000b000 fe:00 1649 /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 40 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 40 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd ex mr mw me
+70075859b000-70075859c000 rw-p 00015000 fe:00 1649 /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075859c000-70075859e000 r--p 00016000 fe:00 1649 /system/lib64/android.frameworks.schedulerservice@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007585ae000-7007585ce000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007585ce000-7007585d1000 r--p 00000000 fe:00 1634 /system/lib64/libnetutils.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007585d1000-7007585d5000 r-xp 00003000 fe:00 1634 /system/lib64/libnetutils.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007585d5000-7007585d6000 rw-p 00007000 fe:00 1634 /system/lib64/libnetutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007585d6000-7007585d7000 r--p 00008000 fe:00 1634 /system/lib64/libnetutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007585d7000-7007585d8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007585e6000-7007585e7000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007585e7000-7007585ef000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007585ef000-7007585f0000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007585f0000-7007585f3000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007585f3000-7007585f4000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007585f4000-7007585fc000 rw-s 00000000 fe:10 24584 /data/system_ce/0/accounts_ce.db-shm
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr sh mr mw me ms
+7007585fc000-7007585fd000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007585fd000-700758605000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758605000-70075862a000 r--p 00000000 fe:00 1626 /system/lib64/android.hardware.broadcastradio@1.1.so
+Size: 148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 68 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 68 kB
+Private_Dirty: 0 kB
+Referenced: 68 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 68 kB
+VmFlags: rd mr mw me
+70075862a000-70075865c000 r-xp 00025000 fe:00 1626 /system/lib64/android.hardware.broadcastradio@1.1.so
+Size: 200 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 196 kB
+Pss: 196 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 196 kB
+Private_Dirty: 0 kB
+Referenced: 196 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 196 kB
+VmFlags: rd ex mr mw me
+70075865c000-70075865d000 rw-p 00057000 fe:00 1626 /system/lib64/android.hardware.broadcastradio@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075865d000-700758664000 r--p 00058000 fe:00 1626 /system/lib64/android.hardware.broadcastradio@1.1.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd mr mw me ac
+700758666000-700758667000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758667000-70075866f000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075866f000-700758674000 r--s 004e8000 fe:00 1805 /system/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 10 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr me ms
+70075869d000-7007586a6000 r--p 00000000 fe:00 1249 /system/lib64/libinputservice.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 24 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 24 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr mw me
+7007586a6000-7007586ad000 r-xp 00009000 fe:00 1249 /system/lib64/libinputservice.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007586ad000-7007586ae000 rw-p 00010000 fe:00 1249 /system/lib64/libinputservice.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007586ae000-7007586af000 r--p 00011000 fe:00 1249 /system/lib64/libinputservice.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007586b2000-7007586b3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007586b3000-7007586b6000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007586b6000-7007586b7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007586d2000-7007586df000 r--p 00000000 fe:00 1211 /system/lib64/android.hardware.sensors@1.0.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+7007586df000-7007586f1000 r-xp 0000d000 fe:00 1211 /system/lib64/android.hardware.sensors@1.0.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 72 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 72 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 72 kB
+VmFlags: rd ex mr mw me
+7007586f1000-7007586f2000 rw-p 0001f000 fe:00 1211 /system/lib64/android.hardware.sensors@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007586f2000-7007586f4000 r--p 00020000 fe:00 1211 /system/lib64/android.hardware.sensors@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007586f4000-7007586f5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007586f5000-7007586f8000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007586f8000-7007586f9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758721000-70075872e000 r--p 00000000 fe:00 1212 /system/lib64/libkeystore_binder.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 17 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 17 kB
+VmFlags: rd mr mw me
+70075872e000-700758739000 r-xp 0000d000 fe:00 1212 /system/lib64/libkeystore_binder.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 14 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd ex mr mw me
+700758739000-70075873a000 rw-p 00018000 fe:00 1212 /system/lib64/libkeystore_binder.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075873a000-70075873d000 r--p 00019000 fe:00 1212 /system/lib64/libkeystore_binder.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+70075873d000-70075873e000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758740000-700758742000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758742000-700758754000 r--p 00000000 fe:00 1521 /system/lib64/android.hardware.contexthub@1.0.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+700758754000-70075876e000 r-xp 00012000 fe:00 1521 /system/lib64/android.hardware.contexthub@1.0.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 100 kB
+Pss: 100 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 100 kB
+Private_Dirty: 0 kB
+Referenced: 100 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 100 kB
+VmFlags: rd ex mr mw me
+70075876e000-70075876f000 rw-p 0002c000 fe:00 1521 /system/lib64/android.hardware.contexthub@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075876f000-700758772000 r--p 0002d000 fe:00 1521 /system/lib64/android.hardware.contexthub@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+7007587a6000-7007587a8000 r--p 00000000 fe:00 1270 /system/lib64/libschedulerservicehidl.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007587a8000-7007587a9000 r-xp 00002000 fe:00 1270 /system/lib64/libschedulerservicehidl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+7007587a9000-7007587aa000 rw-p 00003000 fe:00 1270 /system/lib64/libschedulerservicehidl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007587aa000-7007587ab000 r--p 00004000 fe:00 1270 /system/lib64/libschedulerservicehidl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007587ab000-7007587ae000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587ae000-7007587af000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587af000-7007587b2000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587b2000-7007587b3000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587b3000-7007587b6000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587b6000-7007587b7000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587b7000-7007587ba000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587ba000-7007587bb000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587bb000-7007587be000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587be000-7007587bf000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587bf000-7007587c2000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587c2000-7007587c3000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587c3000-7007587cd000 r--p 00000000 fe:00 1157 /system/lib64/android.hardware.light@2.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+7007587cd000-7007587d7000 r-xp 0000a000 fe:00 1157 /system/lib64/android.hardware.light@2.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 40 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 40 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd ex mr mw me
+7007587d7000-7007587d8000 rw-p 00014000 fe:00 1157 /system/lib64/android.hardware.light@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007587d8000-7007587da000 r--p 00015000 fe:00 1157 /system/lib64/android.hardware.light@2.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007587db000-7007587dd000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587dd000-7007587e0000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587e0000-7007587e1000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587e1000-7007587e4000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587e4000-7007587e5000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587e5000-7007587e8000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587e8000-7007587e9000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587e9000-7007587ec000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587ec000-7007587ed000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007587ed000-7007587f0000 r-xp 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me ac
+7007587f0000-7007587f1000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758808000-700758814000 r--p 00000000 fe:00 1677 /system/lib64/android.hardware.vibrator@1.2.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+700758814000-70075881f000 r-xp 0000c000 fe:00 1677 /system/lib64/android.hardware.vibrator@1.2.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 44 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 44 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 44 kB
+VmFlags: rd ex mr mw me
+70075881f000-700758820000 rw-p 00017000 fe:00 1677 /system/lib64/android.hardware.vibrator@1.2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758820000-700758822000 r--p 00018000 fe:00 1677 /system/lib64/android.hardware.vibrator@1.2.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758830000-700758850000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 32 kB
+Referenced: 32 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd wr mr mw me ac
+700758850000-700758858000 r--p 00000000 fe:00 1546 /system/lib64/libsensorservicehidl.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 24 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 24 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr mw me
+700758858000-70075885c000 r-xp 00008000 fe:00 1546 /system/lib64/libsensorservicehidl.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd ex mr mw me
+70075885c000-70075885d000 rw-p 0000c000 fe:00 1546 /system/lib64/libsensorservicehidl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075885d000-70075885f000 r--p 0000d000 fe:00 1546 /system/lib64/libsensorservicehidl.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758860000-700758880000 r--s 00000000 00:13 6738 /dev/__properties__/u:object_r:system_boot_reason_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700758880000-700758897000 r--p 00000000 fe:00 1225 /system/lib64/libkeystore_aidl.so
+Size: 92 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 92 kB
+Pss: 32 kB
+Shared_Clean: 92 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 92 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me
+700758897000-7007588a4000 r-xp 00017000 fe:00 1225 /system/lib64/libkeystore_aidl.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 17 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 17 kB
+VmFlags: rd ex mr mw me
+7007588a4000-7007588a5000 rw-p 00024000 fe:00 1225 /system/lib64/libkeystore_aidl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007588a5000-7007588ad000 r--p 00025000 fe:00 1225 /system/lib64/libkeystore_aidl.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 32 kB
+Referenced: 32 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me ac
+7007588ad000-7007588ae000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007588ae000-7007588b0000 r-xp 00000000 00:00 0
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me ac
+7007588b0000-7007588d0000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+7007588d0000-7007588d6000 r--p 00000000 fe:00 1704 /system/lib64/libkeystore_parcelables.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 7 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007588d6000-7007588da000 r-xp 00006000 fe:00 1704 /system/lib64/libkeystore_parcelables.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007588da000-7007588db000 rw-p 0000a000 fe:00 1704 /system/lib64/libkeystore_parcelables.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007588db000-7007588dc000 r--p 0000b000 fe:00 1704 /system/lib64/libkeystore_parcelables.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007588dd000-7007588de000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007588de000-7007588e6000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007588e6000-700758906000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700758906000-70075890e000 rw-s 00000000 fe:10 188581 /data/system/notification_log.db-shm
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr sh mr mw me ms
+70075890e000-70075892d000 r--p 00000000 fe:00 1578 /system/lib64/android.hardware.broadcastradio@1.0.so
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+70075892d000-70075895a000 r-xp 0001f000 fe:00 1578 /system/lib64/android.hardware.broadcastradio@1.0.so
+Size: 180 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 176 kB
+Pss: 176 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 176 kB
+Private_Dirty: 0 kB
+Referenced: 176 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 176 kB
+VmFlags: rd ex mr mw me
+70075895a000-70075895b000 rw-p 0004c000 fe:00 1578 /system/lib64/android.hardware.broadcastradio@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075895b000-700758961000 r--p 0004d000 fe:00 1578 /system/lib64/android.hardware.broadcastradio@1.0.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 24 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 24 kB
+Referenced: 24 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr mw me ac
+700758961000-700758962000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758962000-700758965000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758965000-700758966000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758966000-700758968000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758983000-700758984000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758984000-70075898c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075898c000-70075898d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075898d000-700758990000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700758990000-700758991000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758991000-700758993000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007589b3000-7007589b4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007589b4000-7007589bc000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007589bc000-7007589f8000 r--p 00000000 fe:00 1622 /system/lib64/libandroid_servers.so
+Size: 240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 100 kB
+Pss: 100 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 100 kB
+Private_Dirty: 0 kB
+Referenced: 100 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 100 kB
+VmFlags: rd mr mw me
+7007589f8000-700758a36000 r-xp 0003c000 fe:00 1622 /system/lib64/libandroid_servers.so
+Size: 248 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 244 kB
+Pss: 244 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 244 kB
+Private_Dirty: 0 kB
+Referenced: 244 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 244 kB
+VmFlags: rd ex mr mw me
+700758a36000-700758a37000 rw-p 0007a000 fe:00 1622 /system/lib64/libandroid_servers.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758a37000-700758a3f000 r--p 0007b000 fe:00 1622 /system/lib64/libandroid_servers.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 32 kB
+Referenced: 32 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me ac
+700758a3f000-700758a40000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758a40000-700758a41000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758a41000-700758a49000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758a49000-700758a4a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758a4a000-700758a52000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758a52000-700758a5c000 r--p 00000000 fe:00 1507 /system/lib64/android.hardware.ir@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700758a5c000-700758a66000 r-xp 0000a000 fe:00 1507 /system/lib64/android.hardware.ir@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 40 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 40 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd ex mr mw me
+700758a66000-700758a67000 rw-p 00014000 fe:00 1507 /system/lib64/android.hardware.ir@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758a67000-700758a69000 r--p 00015000 fe:00 1507 /system/lib64/android.hardware.ir@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700758a69000-700758a6a000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758a6a000-700758a6d000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758a6d000-700758a6e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758a6e000-700758a8e000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 36 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 36 kB
+Referenced: 36 kB
+Anonymous: 36 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 36 kB
+VmFlags: rd wr mr mw me ac
+700758a8e000-700758a8f000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700758a8f000-700758a90000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700758a90000-700758b88000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me nr
+700758b88000-700758b89000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700758b89000-700758b8a000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+700758b8a000-700758c82000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me nr
+700758c82000-700758c85000 r--p 00000000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+700758c85000-700758c89000 r-xp 00003000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700758c89000-700758c8a000 r--p 00007000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700758c8a000-700758c8b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758c8b000-700758c90000 r--s 00000000 fe:00 3249 /system/framework/oat/x86_64/com.android.location.provider.impl.vdex
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 10 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr me ms
+700758c90000-700758c91000 r--p 00008000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+700758c91000-700758c92000 rw-p 00009000 fe:00 3277 /system/framework/oat/x86_64/com.android.location.provider.impl.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700758c93000-700758c94000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758c94000-700758c97000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758c97000-700758c98000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700758c98000-700758c99000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758c99000-700758ca1000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758ca1000-700758ca2000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700758ca2000-700758caa000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700758caa000-700758cca000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700758cca000-700758d93000 r--p 00000000 fe:00 3287 /system/framework/oat/x86_64/wifi-service.odex
+Size: 804 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 116 kB
+Pss: 116 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 116 kB
+Private_Dirty: 0 kB
+Referenced: 116 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 116 kB
+VmFlags: rd mr mw me
+700758d93000-70075910d000 r-xp 000c9000 fe:00 3287 /system/framework/oat/x86_64/wifi-service.odex
+Size: 3560 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 3184 kB
+Pss: 3184 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 3184 kB
+Private_Dirty: 0 kB
+Referenced: 3184 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3184 kB
+VmFlags: rd ex mr mw me
+70075910d000-70075910f000 r--p 00443000 fe:00 3287 /system/framework/oat/x86_64/wifi-service.odex
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+70075910f000-700759118000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 36 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 36 kB
+Referenced: 36 kB
+Anonymous: 36 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 36 kB
+VmFlags: rd wr mr mw me ac
+700759118000-70075932e000 r--s 00000000 fe:00 3231 /system/framework/oat/x86_64/wifi-service.vdex
+Size: 2136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 64 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 64 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 64 kB
+VmFlags: rd mr me ms
+70075932e000-70075932f000 r--p 00445000 fe:00 3287 /system/framework/oat/x86_64/wifi-service.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+70075932f000-700759330000 rw-p 00446000 fe:00 3287 /system/framework/oat/x86_64/wifi-service.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700759330000-700759332000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700759332000-700759348000 r--s 00602000 fe:00 1821 /system/priv-app/Telecom/Telecom.apk
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 88 kB
+Pss: 88 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 88 kB
+Private_Dirty: 0 kB
+Referenced: 88 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 88 kB
+VmFlags: rd mr me ms
+700759348000-700759349000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700759349000-700759351000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700759351000-700759355000 r--p 00000000 fe:00 3250 /system/framework/oat/x86_64/ethernet-service.odex
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 16 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700759355000-700759361000 r-xp 00004000 fe:00 3250 /system/framework/oat/x86_64/ethernet-service.odex
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 48 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 48 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 48 kB
+VmFlags: rd ex mr mw me
+700759361000-700759362000 r--p 00010000 fe:00 3250 /system/framework/oat/x86_64/ethernet-service.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700759362000-700759363000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700759363000-70075936b000 r--s 00000000 fe:00 3243 /system/framework/oat/x86_64/ethernet-service.vdex
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 32 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr me ms
+70075936b000-70075936c000 r--p 00011000 fe:00 3250 /system/framework/oat/x86_64/ethernet-service.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+70075936c000-70075936d000 rw-p 00012000 fe:00 3250 /system/framework/oat/x86_64/ethernet-service.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075936d000-70075936f000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075936f000-700759370000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700759370000-700759373000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700759373000-700759375000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700759375000-700759378000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700759378000-700759379000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700759379000-70075937a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70075937a000-700759382000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700759382000-700759383000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700759383000-700759386000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700759386000-700759387000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700759387000-70075983a000 r--p 00000000 fe:00 3248 /system/framework/oat/x86_64/services.odex
+Size: 4812 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2576 kB
+Pss: 2576 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 2576 kB
+Private_Dirty: 0 kB
+Referenced: 2576 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2576 kB
+VmFlags: rd mr mw me
+70075983a000-70075a9b3000 r-xp 004b3000 fe:00 3248 /system/framework/oat/x86_64/services.odex
+Size: 17892 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 17060 kB
+Pss: 17060 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 17060 kB
+Private_Dirty: 0 kB
+Referenced: 17060 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 17060 kB
+VmFlags: rd ex mr mw me
+70075a9b3000-70075a9ba000 r--p 0162c000 fe:00 3248 /system/framework/oat/x86_64/services.odex
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd mr mw me ac
+70075a9ba000-70075a9e2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 160 kB
+Pss: 160 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 160 kB
+Referenced: 160 kB
+Anonymous: 160 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 160 kB
+VmFlags: rd wr mr mw me ac
+70075a9e2000-70075b335000 r--s 00000000 fe:00 3271 /system/framework/oat/x86_64/services.vdex
+Size: 9548 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2856 kB
+Pss: 2856 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 2856 kB
+Private_Dirty: 0 kB
+Referenced: 2856 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2856 kB
+VmFlags: rd mr me ms
+70075b335000-70075b336000 r--p 01633000 fe:00 3248 /system/framework/oat/x86_64/services.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+70075b336000-70075b337000 rw-p 01634000 fe:00 3248 /system/framework/oat/x86_64/services.odex
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075b338000-70075b33a000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b33a000-70075b33b000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70075b33b000-70075b343000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b344000-70075b346000 r-xp 00000000 00:00 0
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me ac
+70075b34b000-70075b34c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b34c000-70075b34f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b34f000-70075b350000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b350000-70075b352000 r-xp 00000000 00:00 0
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me ac
+70075b354000-70075b355000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b355000-70075b358000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b358000-70075b359000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b359000-70075b35a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70075b35a000-70075b362000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b362000-70075b363000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b363000-70075b366000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b366000-70075b367000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70075b367000-70075b369000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70075b369000-70075b36a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70075b36a000-70075b372000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b372000-70075b373000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70075b373000-70075b37b000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70075b37b000-70075b479000 r--p 00000000 00:13 6783 /dev/binder
+Size: 1016 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 24 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 24 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr me dc nr mm
+70075b479000-70075b47a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b47a000-70075b47b000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b47b000-70075b57f000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075b57f000-70075b580000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b580000-70075b581000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b581000-70075b685000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70075b685000-70075b686000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b686000-70075b687000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b687000-70075b78b000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me nr
+70075b78b000-70075b78c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b78c000-70075b78d000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70075b78d000-70075b891000 rw-p 00000000 00:00 0
+Size: 1040 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70075b891000-70075d891000 r--s 02000000 00:05 9572 /memfd:/jit-cache (deleted)
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd sh mr mw me ms
+70075d891000-700766592000 ---p 00000000 00:00 0
+Size: 144388 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766592000-700766593000 r--p 00000000 fe:00 1106 /system/lib64/libwebviewchromium_loader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+700766593000-700766594000 r-xp 00001000 fe:00 1106 /system/lib64/libwebviewchromium_loader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700766594000-700766595000 rw-p 00002000 fe:00 1106 /system/lib64/libwebviewchromium_loader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766595000-700766596000 r--p 00003000 fe:00 1106 /system/lib64/libwebviewchromium_loader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766596000-700766597000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766597000-700766598000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766598000-7007665a0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665a0000-7007665a1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665a1000-7007665a4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665a4000-7007665a5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665a5000-7007665a9000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007665ab000-7007665af000 r--s 00002000 fe:00 2036 /system/priv-app/FusedLocation/FusedLocation.apk
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 14 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd mr me ms
+7007665af000-7007665b2000 r--p 00000000 fe:00 2036 /system/priv-app/FusedLocation/FusedLocation.apk
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 10 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me ac
+7007665b5000-7007665b7000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007665b7000-7007665b8000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665b8000-7007665bb000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665bb000-7007665bc000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665bc000-7007665be000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007665be000-7007665bf000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007665bf000-7007665c7000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665c7000-7007665c8000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007665c8000-7007665d0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665d0000-7007665d1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665d1000-7007665d4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665d4000-7007665d6000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665d6000-7007665d9000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665d9000-7007665da000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007665da000-7007665db000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007665db000-7007665e3000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665e3000-7007665e4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007665e4000-7007665ec000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665ed000-7007665ef000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665ef000-7007665f0000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007665f0000-7007665f8000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007665f8000-700766646000 r--s 00000000 fe:00 1030 /system/usr/hyphen-data/hyph-hu.hyb
+Size: 312 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700766646000-70076664f000 r--p 00000000 fe:00 1712 /system/lib64/libcompiler_rt.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 9 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+70076664f000-700766664000 r-xp 00009000 fe:00 1712 /system/lib64/libcompiler_rt.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700766664000-700766665000 rw-p 0001e000 fe:00 1712 /system/lib64/libcompiler_rt.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766665000-700766666000 r--p 0001f000 fe:00 1712 /system/lib64/libcompiler_rt.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766666000-700766690000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 168 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766691000-700766699000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700766699000-70076669b000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076669b000-70076669c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076669c000-70076669f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076669f000-7007666a0000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007666a0000-7007666b0000 rw-s 00000000 00:05 11661 /dev/ashmem/RemoteDataSource (deleted)
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr sh mr mw me ms
+7007666b0000-7007666b1000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007666b1000-7007666b9000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007666b9000-7007666ba000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007666ba000-7007666bd000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007666bd000-7007666be000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007666be000-7007666c0000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007666c0000-700766751000 r--p 00000000 fe:30 333 /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size: 580 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 192 kB
+Pss: 38 kB
+Shared_Clean: 192 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 192 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 38 kB
+VmFlags: rd mr mw me
+700766751000-70076690e000 r-xp 00091000 fe:30 333 /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size: 1780 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 116 kB
+Pss: 24 kB
+Shared_Clean: 116 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 116 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd ex mr mw me
+70076690e000-70076690f000 rw-p 0024e000 fe:30 333 /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076690f000-70076691f000 r--p 0024f000 fe:30 333 /vendor/lib64/egl/libGLESv1_CM_swiftshader.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 64 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 64 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+70076691f000-70076696d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 312 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 312 kB
+Pss: 17 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 312 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 312 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 17 kB
+VmFlags: rd wr mr mw me ac
+70076696d000-70076696e000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076696e000-700766976000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766976000-700766977000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766977000-70076697f000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076697f000-700766980000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766980000-700766983000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766983000-700766985000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766985000-700766988000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766988000-70076698a000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076698a000-70076698d000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076698d000-70076698f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076698f000-700766992000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766992000-700766993000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766993000-700766994000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766994000-70076699c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076699c000-700766a5f000 r--p 00000000 fe:30 335 /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size: 780 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 51 kB
+Shared_Clean: 256 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 256 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 51 kB
+VmFlags: rd mr mw me
+700766a5f000-700766c8d000 r-xp 000c3000 fe:30 335 /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size: 2232 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2212 kB
+Pss: 624 kB
+Shared_Clean: 2212 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2212 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 624 kB
+VmFlags: rd ex mr mw me
+700766c8d000-700766c8e000 rw-p 002f1000 fe:30 335 /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700766c8e000-700766ca2000 r--p 002f2000 fe:30 335 /vendor/lib64/egl/libGLESv2_swiftshader.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 80 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 80 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700766ca2000-700766cf0000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 312 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 312 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 296 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 44 kB
+Anonymous: 312 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd wr mr mw me ac
+700766cf1000-700766cf3000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766cf3000-700766cf4000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766cf4000-700766cf7000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766cf7000-700766cf9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766cf9000-700766cfc000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766cfc000-700766cfd000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766cfd000-700766cfe000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766cfe000-700766d06000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766d06000-700766d13000 r--p 00000000 fe:30 334 /vendor/lib64/egl/libEGL_swiftshader.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 10 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+700766d13000-700766d2f000 r-xp 0000d000 fe:30 334 /vendor/lib64/egl/libEGL_swiftshader.so
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 22 kB
+Shared_Clean: 112 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 112 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 22 kB
+VmFlags: rd ex mr mw me
+700766d2f000-700766d30000 rw-p 00029000 fe:30 334 /vendor/lib64/egl/libEGL_swiftshader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766d30000-700766d31000 r--p 0002a000 fe:30 334 /vendor/lib64/egl/libEGL_swiftshader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766d31000-700766d78000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 284 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 284 kB
+Pss: 19 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 280 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 284 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 19 kB
+VmFlags: rd wr mr mw me ac
+700766d78000-700766d7e000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+700766d7e000-700766d7f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766d7f000-700766d82000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766d82000-700766d83000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766d83000-700766d84000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766d84000-700766d8c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766d8c000-700766d8d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766d8d000-700766d90000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766d90000-700766d91000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766d91000-700766db1000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 124 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 124 kB
+Referenced: 124 kB
+Anonymous: 124 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 124 kB
+VmFlags: rd wr mr mw me ac
+700766db1000-700766db3000 r--p 00000000 fe:00 1488 /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+700766db3000-700766db5000 r-xp 00002000 fe:00 1488 /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700766db5000-700766db6000 rw-p 00004000 fe:00 1488 /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766db6000-700766db7000 r--p 00005000 fe:00 1488 /system/lib64/vndk-sp-Q/libbinderthreadstate.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766db7000-700766db8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766db8000-700766dbc000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700766dbc000-700766dbd000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766dbd000-700766dc5000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766dc5000-700766dc6000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766dc6000-700766dce000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766dce000-700766e26000 r--p 00000000 fe:00 1497 /system/lib64/vndk-sp-Q/libc++.so
+Size: 352 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 252 kB
+Pss: 11 kB
+Shared_Clean: 252 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 252 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd mr mw me
+700766e26000-700766e9a000 r-xp 00058000 fe:00 1497 /system/lib64/vndk-sp-Q/libc++.so
+Size: 464 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 408 kB
+Pss: 18 kB
+Shared_Clean: 408 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 408 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 18 kB
+VmFlags: rd ex mr mw me
+700766e9a000-700766e9b000 rw-p 000cc000 fe:00 1497 /system/lib64/vndk-sp-Q/libc++.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766e9b000-700766ea3000 r--p 000cd000 fe:00 1497 /system/lib64/vndk-sp-Q/libc++.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 32 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700766ea3000-700766ea7000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+700766ea8000-700766ea9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766ea9000-700766eac000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766eac000-700766ead000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766ead000-700766eae000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766eae000-700766eb6000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766eb6000-700766ed6000 r--s 00000000 00:13 6703 /dev/__properties__/u:object_r:device_logging_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr me ms
+700766ed6000-700766ee9000 r--p 00000000 fe:00 1476 /system/lib64/vndk-sp-Q/libhwbinder.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+700766ee9000-700766efa000 r-xp 00013000 fe:00 1476 /system/lib64/vndk-sp-Q/libhwbinder.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700766efa000-700766efb000 rw-p 00024000 fe:00 1476 /system/lib64/vndk-sp-Q/libhwbinder.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766efb000-700766efd000 r--p 00025000 fe:00 1476 /system/lib64/vndk-sp-Q/libhwbinder.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766efd000-700766efe000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766eff000-700766f01000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700766f01000-700766f02000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f02000-700766f05000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f05000-700766f07000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f07000-700766f0a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f0a000-700766f0b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f0c000-700766f0d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f0d000-700766f10000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f10000-700766f11000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f11000-700766f13000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700766f13000-700766f14000 r--p 00000000 fe:00 1492 /system/lib64/vndk-sp-Q/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+700766f14000-700766f15000 r-xp 00001000 fe:00 1492 /system/lib64/vndk-sp-Q/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700766f15000-700766f16000 rw-p 00002000 fe:00 1492 /system/lib64/vndk-sp-Q/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f16000-700766f17000 r--p 00003000 fe:00 1492 /system/lib64/vndk-sp-Q/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766f17000-700766f37000 r--s 00000000 00:13 6734 /dev/__properties__/u:object_r:safemode_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700766f37000-700766f57000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700766f57000-700766f63000 r--p 00000000 fe:00 1491 /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 6 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+700766f63000-700766f6f000 r-xp 0000c000 fe:00 1491 /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 6 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+700766f6f000-700766f70000 rw-p 00018000 fe:00 1491 /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f70000-700766f72000 r--p 00019000 fe:00 1491 /system/lib64/vndk-sp-Q/android.hardware.graphics.mapper@2.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766f72000-700766f74000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700766f74000-700766f75000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766f75000-700766f7d000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f7d000-700766f7e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f7e000-700766f81000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f81000-700766f82000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766f82000-700766f83000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766f83000-700766f8b000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f8b000-700766f8e000 r--p 00000000 fe:30 348 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700766f8e000-700766f91000 r-xp 00003000 fe:30 348 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 2 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+700766f91000-700766f92000 rw-p 00006000 fe:30 348 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766f92000-700766f93000 r--p 00007000 fe:30 348 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766f93000-700766f94000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700766f95000-700766f99000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700766f99000-700766f9a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766f9a000-700766fa2000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766fa2000-700766fa3000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700766fa3000-700766fab000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766fab000-700766fcb000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700766fcb000-700766fd2000 r--p 00000000 fe:00 1487 /system/lib64/vndk-sp-Q/libcutils.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700766fd2000-700766fdb000 r-xp 00007000 fe:00 1487 /system/lib64/vndk-sp-Q/libcutils.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 2 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+700766fdb000-700766fdc000 rw-p 00010000 fe:00 1487 /system/lib64/vndk-sp-Q/libcutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766fdc000-700766fde000 r--p 00011000 fe:00 1487 /system/lib64/vndk-sp-Q/libcutils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700766fde000-700766fdf000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766fe0000-700766fe1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766fe1000-700766fe4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700766fe4000-700766fe5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700766fe5000-700767005000 r--s 00000000 00:13 6741 /dev/__properties__/u:object_r:system_radio_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700767005000-70076700e000 r--p 00000000 fe:00 1503 /system/lib64/vndk-sp-Q/libbase.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 1 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+70076700e000-700767017000 r-xp 00009000 fe:00 1503 /system/lib64/vndk-sp-Q/libbase.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700767017000-700767018000 rw-p 00012000 fe:00 1503 /system/lib64/vndk-sp-Q/libbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767018000-700767019000 r--p 00013000 fe:00 1503 /system/lib64/vndk-sp-Q/libbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700767019000-70076701a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076701b000-70076701d000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076701d000-70076701e000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076701e000-700767026000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767026000-700767027000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767027000-70076702a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076702a000-70076702b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076702b000-70076702c000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076702c000-700767034000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767034000-700767035000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767035000-700767038000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767038000-700767039000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767039000-70076703a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076703a000-700767042000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767042000-700767043000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700767043000-70076704b000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076704b000-70076705a000 r--p 00000000 fe:00 1478 /system/lib64/vndk-sp-Q/libutils.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 2 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+70076705a000-700767066000 r-xp 0000f000 fe:00 1478 /system/lib64/vndk-sp-Q/libutils.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 2 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+700767066000-700767067000 rw-p 0001b000 fe:00 1478 /system/lib64/vndk-sp-Q/libutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767067000-700767068000 r--p 0001c000 fe:00 1478 /system/lib64/vndk-sp-Q/libutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700767068000-700767069000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076706a000-70076706c000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076706c000-70076706d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076706d000-700767070000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767070000-700767072000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767072000-700767075000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767075000-700767077000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767077000-70076707a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076707a000-70076707b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076707b000-70076707c000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076707c000-700767084000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767084000-700767085000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700767085000-70076708d000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076708e000-700767094000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700767094000-700767095000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767095000-700767098000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767098000-700767099000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767099000-70076709b000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076709b000-7007670d4000 r--p 00000000 fe:00 1489 /system/lib64/vndk-sp-Q/libhidltransport.so
+Size: 228 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 148 kB
+Pss: 10 kB
+Shared_Clean: 148 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 148 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007670d4000-70076712f000 r-xp 00039000 fe:00 1489 /system/lib64/vndk-sp-Q/libhidltransport.so
+Size: 364 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 236 kB
+Pss: 13 kB
+Shared_Clean: 236 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 236 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 13 kB
+VmFlags: rd ex mr mw me
+70076712f000-700767130000 rw-p 00094000 fe:00 1489 /system/lib64/vndk-sp-Q/libhidltransport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767130000-70076713a000 r--p 00095000 fe:00 1489 /system/lib64/vndk-sp-Q/libhidltransport.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 40 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 40 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me ac
+70076713a000-70076713b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076713b000-70076713c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076713c000-70076713f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076713f000-700767141000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767141000-700767144000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767144000-700767145000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767145000-700767146000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700767146000-70076714e000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076714e000-70076714f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076714f000-700767152000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767152000-700767153000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700767153000-700767154000 r--p 00000000 fe:00 1495 /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+700767154000-700767155000 r-xp 00001000 fe:00 1495 /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700767155000-700767156000 rw-p 00002000 fe:00 1495 /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700767156000-700767157000 r--p 00003000 fe:00 1495 /system/lib64/vndk-sp-Q/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700767157000-700767197000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 152 kB
+Pss: 152 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 152 kB
+Referenced: 152 kB
+Anonymous: 152 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 152 kB
+VmFlags: rd wr mr mw me ac
+700767197000-7007671a6000 r--p 00000000 fe:00 1477 /system/lib64/vndk-sp-Q/libhidlbase.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 3 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007671a6000-7007671b9000 r-xp 0000f000 fe:00 1477 /system/lib64/vndk-sp-Q/libhidlbase.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 1 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+7007671b9000-7007671ba000 rw-p 00022000 fe:00 1477 /system/lib64/vndk-sp-Q/libhidlbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671ba000-7007671bc000 r--p 00023000 fe:00 1477 /system/lib64/vndk-sp-Q/libhidlbase.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007671bc000-7007671bd000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671bd000-7007671bf000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007671bf000-7007671c0000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007671c0000-7007671c8000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671c8000-7007671c9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007671c9000-7007671cc000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671cc000-7007671cd000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007671cd000-7007671ce000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007671ce000-7007671d6000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671d6000-7007671d7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007671d7000-7007671da000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007671da000-7007671db000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007671db000-700768d59000 r--s 00bc3000 fe:00 3346 /system/framework/framework-res.apk
+Size: 28152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 19968 kB
+Pss: 1144 kB
+Shared_Clean: 19956 kB
+Shared_Dirty: 0 kB
+Private_Clean: 12 kB
+Private_Dirty: 0 kB
+Referenced: 19968 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1144 kB
+VmFlags: rd mr me ms
+700768d59000-700768e14000 r--s 0287a000 fe:00 3346 /system/framework/framework-res.apk
+Size: 748 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 144 kB
+Pss: 18 kB
+Shared_Clean: 144 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 144 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 18 kB
+VmFlags: rd mr me ms
+700768e14000-700768e35000 r--p 00000000 fe:00 1638 /system/lib64/libssl.so
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 108 kB
+Pss: 38 kB
+Shared_Clean: 108 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 108 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 38 kB
+VmFlags: rd mr mw me
+700768e35000-700768e6c000 r-xp 00021000 fe:00 1638 /system/lib64/libssl.so
+Size: 220 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 172 kB
+Pss: 88 kB
+Shared_Clean: 168 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 172 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 88 kB
+VmFlags: rd ex mr mw me
+700768e6c000-700768e6d000 rw-p 00058000 fe:00 1638 /system/lib64/libssl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768e6d000-700768e70000 r--p 00059000 fe:00 1638 /system/lib64/libssl.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700768e70000-700768e74000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700768e74000-700768e75000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700768e75000-700768e7d000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768e7d000-700768e7e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700768e7e000-700768e81000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768e81000-700768e82000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700768e82000-700768ea2000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700768ea2000-700768ebf000 r--p 00000000 fe:00 1513 /system/lib64/libjavacrypto.so
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 23 kB
+Shared_Clean: 112 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 112 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 23 kB
+VmFlags: rd mr mw me
+700768ebf000-700768ee5000 r-xp 0001d000 fe:00 1513 /system/lib64/libjavacrypto.so
+Size: 152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 152 kB
+Pss: 27 kB
+Shared_Clean: 152 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 152 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 27 kB
+VmFlags: rd ex mr mw me
+700768ee5000-700768ee7000 rw-p 00043000 fe:00 1513 /system/lib64/libjavacrypto.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768ee7000-700768ee9000 r--p 00045000 fe:00 1513 /system/lib64/libjavacrypto.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700768ee9000-700768eea000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768eea000-700768eeb000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700768eeb000-700768ef3000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768ef3000-700768ef4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700768ef4000-700768efc000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768efc000-700768f1c000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700768f1c000-700768f22000 r--p 00000000 fe:00 1763 /system/lib64/libsoundpool.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 4 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+700768f22000-700768f27000 r-xp 00006000 fe:00 1763 /system/lib64/libsoundpool.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 6 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+700768f27000-700768f28000 rw-p 0000b000 fe:00 1763 /system/lib64/libsoundpool.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768f28000-700768f29000 r--p 0000c000 fe:00 1763 /system/lib64/libsoundpool.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700768f29000-700768f2a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768f2b000-700768f2c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700768f2c000-700768f2f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768f2f000-700768f31000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700768f31000-700768f34000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768f34000-700768f35000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700768f35000-700768f36000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700768f36000-700768f3e000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700768f3e000-700768f5e000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+700768f5e000-700768fd9000 r--s 00000000 07:08 15 /apex/com.android.tzdata/etc/tz/tzdata
+Size: 492 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700768fd9000-700769748000 r--s 00000000 fe:00 100 /system/fonts/NotoColorEmoji.ttf
+Size: 7612 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700769748000-70076aee5000 r--s 00000000 fe:00 130 /system/fonts/NotoSerifCJK-Regular.ttc
+Size: 24180 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076aee5000-70076c0b5000 r--s 00000000 fe:00 239 /system/fonts/NotoSansCJK-Regular.ttc
+Size: 18240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c0b5000-70076c163000 r--s 00000000 fe:00 161 /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf
+Size: 696 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c163000-70076c1bf000 r--s 00000000 fe:00 257 /system/fonts/NotoSansTibetan-Bold.ttf
+Size: 368 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c1bf000-70076c222000 r--s 00000000 fe:00 270 /system/fonts/NotoSansTibetan-Regular.ttf
+Size: 396 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c222000-70076c29e000 r--s 00000000 fe:00 254 /system/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf
+Size: 496 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c29e000-70076c319000 r--s 00000000 fe:00 196 /system/fonts/NotoSansCuneiform-Regular.ttf
+Size: 492 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c319000-70076c36a000 r--s 00000000 fe:00 233 /system/fonts/RobotoCondensed-BoldItalic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c36a000-70076c3b5000 r--s 00000000 fe:00 282 /system/fonts/RobotoCondensed-Bold.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c3b5000-70076c406000 r--s 00000000 fe:00 288 /system/fonts/RobotoCondensed-MediumItalic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c406000-70076c451000 r--s 00000000 fe:00 184 /system/fonts/RobotoCondensed-Medium.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076c451000-70076c6d1000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 2560 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2452 kB
+Pss: 1171 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 1356 kB
+Private_Clean: 0 kB
+Private_Dirty: 1096 kB
+Referenced: 1336 kB
+Anonymous: 2452 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1171 kB
+VmFlags: rd wr mr mw me ac
+70076c6d1000-70076de00000 r--s 00000000 fe:00 1096 /system/usr/icu/icudt63l.dat
+Size: 23740 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 452 kB
+Pss: 85 kB
+Shared_Clean: 452 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 452 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 85 kB
+VmFlags: rd mr me ms
+70076de00000-70076e200000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 4096 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 3988 kB
+Pss: 3677 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 336 kB
+Private_Clean: 0 kB
+Private_Dirty: 3652 kB
+Referenced: 3632 kB
+Anonymous: 3988 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3677 kB
+VmFlags: rd wr mr mw me ac
+70076e201000-70076e203000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076e203000-70076e204000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e204000-70076e20c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e20c000-70076e25d000 r--s 00000000 fe:00 182 /system/fonts/RobotoCondensed-Italic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076e25d000-70076e2a7000 r--s 00000000 fe:00 96 /system/fonts/RobotoCondensed-Regular.ttf
+Size: 296 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076e2a7000-70076e2f9000 r--s 00000000 fe:00 285 /system/fonts/RobotoCondensed-LightItalic.ttf
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70076e2f9000-70076e2fd000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+70076e2fd000-70076e2fe000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076e2fe000-70076e301000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e301000-70076e303000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076e303000-70076e306000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e306000-70076e307000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076e307000-70076e308000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e308000-70076e310000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e310000-70076e311000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076e311000-70076e314000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e314000-70076e315000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076e315000-70076e316000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e316000-70076e31e000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e31e000-70076e31f000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e31f000-70076e327000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e327000-70076e328000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70076e328000-70076e329000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70076e329000-70076e421000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70076e421000-70076e422000 ---p 00000000 00:00 0 [anon:thread stack guard]
+Name: [anon:thread stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70076e422000-70076e423000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me nr
+70076e423000-70076e51b000 rw-p 00000000 00:00 0
+Size: 992 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me nr
+70076e51b000-70076e51c000 ---p 00000000 00:00 0 [anon:dalvik-Jit thread pool worker thread 0]
+Name: [anon:dalvik-Jit thread pool worker thread 0]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e51c000-70076e51d000 ---p 00000000 00:00 0 [anon:dalvik-Jit thread pool worker thread 0]
+Name: [anon:dalvik-Jit thread pool worker thread 0]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076e51d000-70076e61c000 rw-p 00000000 00:00 0 [anon:dalvik-Jit thread pool worker thread 0]
+Name: [anon:dalvik-Jit thread pool worker thread 0]
+Size: 1020 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 28 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 28 kB
+Referenced: 28 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 28 kB
+VmFlags: rd wr mr mw me ac
+70076e61c000-70076e717000 rw-p 00000000 00:00 0 [anon:dalvik-allocspace non moving space mark-bitmap 1]
+Name: [anon:dalvik-allocspace non moving space mark-bitmap 1]
+Size: 1004 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076e717000-70076e812000 rw-p 00000000 00:00 0 [anon:dalvik-allocspace non moving space live-bitmap 1]
+Name: [anon:dalvik-allocspace non moving space live-bitmap 1]
+Size: 1004 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076e812000-70076e8f1000 r--p 00000000 fe:00 1114 /system/lib64/libart-compiler.so
+Size: 892 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 188 kB
+Pss: 27 kB
+Shared_Clean: 188 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 188 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 27 kB
+VmFlags: rd mr mw me
+70076e8f1000-70076eb46000 r-xp 000df000 fe:00 1114 /system/lib64/libart-compiler.so
+Size: 2388 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2092 kB
+Pss: 201 kB
+Shared_Clean: 2092 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2092 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 201 kB
+VmFlags: rd ex mr mw me
+70076eb46000-70076eb47000 rw-p 00334000 fe:00 1114 /system/lib64/libart-compiler.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076eb47000-70076eb59000 r--p 00335000 fe:00 1114 /system/lib64/libart-compiler.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 72 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 72 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+70076eb59000-70076eb5a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076eb5b000-70076eb5d000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076eb5d000-70076eb5e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076eb5e000-70076eb61000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076eb61000-70076eb62000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076eb62000-70076eb82000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 124 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 124 kB
+Referenced: 124 kB
+Anonymous: 124 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 124 kB
+VmFlags: rd wr mr mw me ac
+70076eb82000-70076eb9c000 r--p 00000000 fe:00 1229 /system/lib64/libopenjdk.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 100 kB
+Pss: 5 kB
+Shared_Clean: 100 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 100 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+70076eb9c000-70076ebb7000 r-xp 0001a000 fe:00 1229 /system/lib64/libopenjdk.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 108 kB
+Pss: 8 kB
+Shared_Clean: 108 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 108 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me
+70076ebb7000-70076ebb9000 rw-p 00035000 fe:00 1229 /system/lib64/libopenjdk.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ebb9000-70076ebba000 r--p 00037000 fe:00 1229 /system/lib64/libopenjdk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+70076ebba000-70076ebbb000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076ebbb000-70076ebbd000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076ebbd000-70076ebbe000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076ebbe000-70076ebc1000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ebc1000-70076ebc2000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70076ebc2000-70076ebe2000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 112 kB
+Pss: 112 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 112 kB
+Referenced: 112 kB
+Anonymous: 112 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 112 kB
+VmFlags: rd wr mr mw me ac
+70076ebe2000-70076ebe6000 r--p 00000000 fe:00 1645 /system/lib64/libopenjdkjvm.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 5 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+70076ebe6000-70076ebea000 r-xp 00004000 fe:00 1645 /system/lib64/libopenjdkjvm.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+70076ebea000-70076ebeb000 rw-p 00008000 fe:00 1645 /system/lib64/libopenjdkjvm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ebeb000-70076ebec000 r--p 00009000 fe:00 1645 /system/lib64/libopenjdkjvm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+70076ebed000-70076ebef000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70076ebef000-70076ebf0000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076ebf0000-70076ebf8000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ebf8000-70076ebf9000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076ebf9000-70076ec01000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ec01000-70076ec02000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+70076ec02000-70076ec0a000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ec0a000-70076ec2a000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70076ec2a000-700770359000 r--s 00000000 fe:00 1096 /system/usr/icu/icudt63l.dat
+Size: 23740 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1164 kB
+Pss: 165 kB
+Shared_Clean: 1164 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 1164 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 165 kB
+VmFlags: rd mr me ms rr
+700770359000-700770371000 r--p 00000000 fe:00 1615 /system/lib64/libjavacore.so
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 92 kB
+Pss: 10 kB
+Shared_Clean: 92 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 92 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+700770371000-700770396000 r-xp 00018000 fe:00 1615 /system/lib64/libjavacore.so
+Size: 148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 148 kB
+Pss: 8 kB
+Shared_Clean: 148 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 148 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me
+700770396000-700770398000 rw-p 0003d000 fe:00 1615 /system/lib64/libjavacore.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770398000-70077039a000 r--p 0003f000 fe:00 1615 /system/lib64/libjavacore.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+70077039a000-70077039b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70077039c000-70077039e000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70077039e000-70077039f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70077039f000-7007703a2000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703a2000-7007703a3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007703a3000-7007703a4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007703a4000-7007703ac000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703ac000-7007703cc000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703cc000-7007703ce000 r--p 00000000 fe:00 1134 /system/lib64/libwebviewchromium_plat_support.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007703ce000-7007703cf000 r-xp 00002000 fe:00 1134 /system/lib64/libwebviewchromium_plat_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007703cf000-7007703d0000 rw-p 00003000 fe:00 1134 /system/lib64/libwebviewchromium_plat_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703d0000-7007703d1000 r--p 00004000 fe:00 1134 /system/lib64/libwebviewchromium_plat_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007703d1000-7007703d2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703d2000-7007703d3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007703d3000-7007703d6000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703d6000-7007703d7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007703d7000-7007703d8000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007703d8000-7007703e0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007703e0000-700770400000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770400000-700770423000 r--p 00000000 fe:00 1228 /system/lib64/android.hardware.renderscript@1.0.so
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 21 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+700770423000-70077047a000 r-xp 00023000 fe:00 1228 /system/lib64/android.hardware.renderscript@1.0.so
+Size: 348 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+70077047a000-70077047b000 rw-p 0007a000 fe:00 1228 /system/lib64/android.hardware.renderscript@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70077047b000-70077047f000 r--p 0007b000 fe:00 1228 /system/lib64/android.hardware.renderscript@1.0.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770480000-700770481000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770481000-700770484000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770484000-700770486000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770486000-700770489000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770489000-70077048a000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70077048a000-7007704aa000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704aa000-7007704b2000 r--p 00000000 fe:00 1184 /system/lib64/libRS.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 10 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007704b2000-7007704bc000 r-xp 00008000 fe:00 1184 /system/lib64/libRS.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007704bc000-7007704bd000 rw-p 00012000 fe:00 1184 /system/lib64/libRS.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704bd000-7007704be000 r--p 00013000 fe:00 1184 /system/lib64/libRS.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007704be000-7007704bf000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704c0000-7007704c2000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007704c2000-7007704c3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007704c3000-7007704c6000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704c6000-7007704c7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007704c7000-7007704c8000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007704c8000-7007704d0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704d0000-7007704d2000 r--p 00000000 fe:00 1768 /system/lib64/libOpenSLES.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007704d2000-7007704d3000 r-xp 00002000 fe:00 1768 /system/lib64/libOpenSLES.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007704d3000-7007704d4000 rw-p 00003000 fe:00 1768 /system/lib64/libOpenSLES.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704d4000-7007704d5000 r--p 00004000 fe:00 1768 /system/lib64/libOpenSLES.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007704d5000-7007704d6000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007704d6000-7007704d9000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704d9000-7007704da000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007704da000-7007704db000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007704db000-7007704e3000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007704e3000-700770503000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 124 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 124 kB
+Referenced: 124 kB
+Anonymous: 124 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 124 kB
+VmFlags: rd wr mr mw me ac
+700770503000-700770505000 r--p 00000000 fe:00 1250 /system/lib64/libOpenMAXAL.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+700770505000-700770506000 r-xp 00002000 fe:00 1250 /system/lib64/libOpenMAXAL.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770506000-700770507000 rw-p 00003000 fe:00 1250 /system/lib64/libOpenMAXAL.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770507000-700770508000 r--p 00004000 fe:00 1250 /system/lib64/libOpenMAXAL.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770508000-700770528000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770528000-700770551000 r--p 00000000 fe:00 1943 /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size: 164 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 88 kB
+Pss: 88 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 88 kB
+Private_Dirty: 0 kB
+Referenced: 88 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 88 kB
+VmFlags: rd mr mw me ac
+700770551000-700770575000 r--p 00000000 fe:00 1470 /system/lib64/libneuralnetworks.so
+Size: 144 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 21 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+700770575000-70077065a000 r-xp 00024000 fe:00 1470 /system/lib64/libneuralnetworks.so
+Size: 916 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+70077065a000-70077065b000 rw-p 00109000 fe:00 1470 /system/lib64/libneuralnetworks.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70077065b000-70077065e000 r--p 0010a000 fe:00 1470 /system/lib64/libneuralnetworks.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+70077065e000-70077096f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 3140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70077096f000-700770973000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700770973000-700770974000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770974000-700770977000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770977000-700770978000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770978000-700770979000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770979000-700770981000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770981000-70077098b000 r--s 00039000 fe:00 1943 /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 40 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 40 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd mr me ms
+70077098b000-7007709ab000 r--p 00000000 fe:00 1724 /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 21 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+7007709ab000-7007709d1000 r-xp 00020000 fe:00 1724 /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size: 152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007709d1000-7007709d2000 rw-p 00046000 fe:00 1724 /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007709d2000-7007709d8000 r--p 00047000 fe:00 1724 /system/lib64/android.hardware.neuralnetworks@1.2.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007709d8000-7007709da000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007709da000-7007709db000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007709db000-7007709de000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007709de000-7007709df000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007709df000-7007709e0000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007709e0000-7007709e8000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007709e8000-700770a08000 r--s 00000000 00:13 6735 /dev/__properties__/u:object_r:serialno_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr me ms
+700770a08000-700770a15000 r--p 00000000 fe:00 1214 /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 17 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 17 kB
+VmFlags: rd mr mw me
+700770a15000-700770a23000 r-xp 0000d000 fe:00 1214 /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770a23000-700770a24000 rw-p 0001b000 fe:00 1214 /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770a24000-700770a26000 r--p 0001c000 fe:00 1214 /system/lib64/android.hardware.neuralnetworks@1.1.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770a26000-700770a2a000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700770a2a000-700770a2b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770a2b000-700770a2e000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770a2e000-700770a2f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770a2f000-700770a30000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770a30000-700770a38000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770a38000-700770a39000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770a39000-700770a3c000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770a3c000-700770a3d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770a3d000-700770a3e000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770a3e000-700770a46000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770a46000-700770a66000 r--s 00000000 00:13 6726 /dev/__properties__/u:object_r:overlay_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700770a66000-700770a80000 r--p 00000000 fe:00 1136 /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 21 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+700770a80000-700770aa1000 r-xp 0001a000 fe:00 1136 /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770aa1000-700770aa2000 rw-p 0003b000 fe:00 1136 /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770aa2000-700770aa7000 r--p 0003c000 fe:00 1136 /system/lib64/android.hardware.neuralnetworks@1.0.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770aa7000-700770aa9000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770aa9000-700770aaa000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770aaa000-700770ab2000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770ab2000-700770ab3000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770ab3000-700770abb000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770abb000-700770abc000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770abc000-700770ac4000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770ac4000-700770ac6000 r--p 00000000 fe:00 1183 /system/lib64/libtextclassifier_hash.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700770ac6000-700770aca000 r-xp 00002000 fe:00 1183 /system/lib64/libtextclassifier_hash.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770aca000-700770acb000 rw-p 00006000 fe:00 1183 /system/lib64/libtextclassifier_hash.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770acb000-700770acc000 r--p 00007000 fe:00 1183 /system/lib64/libtextclassifier_hash.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770acd000-700770b1f000 r--s 00000000 fe:00 211 /system/fonts/Roboto-BoldItalic.ttf
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700770b1f000-700770b20000 r--p 00000000 fe:00 1767 /system/lib64/libjnigraphics.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700770b20000-700770b21000 r-xp 00001000 fe:00 1767 /system/lib64/libjnigraphics.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770b21000-700770b22000 rw-p 00002000 fe:00 1767 /system/lib64/libjnigraphics.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b22000-700770b23000 r--p 00003000 fe:00 1767 /system/lib64/libjnigraphics.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770b24000-700770b26000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b26000-700770b27000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770b27000-700770b2a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b2a000-700770b2b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770b2b000-700770b4b000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 44 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 44 kB
+Referenced: 44 kB
+Anonymous: 44 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 44 kB
+VmFlags: rd wr mr mw me ac
+700770b4b000-700770b4c000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770b4c000-700770b54000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b54000-700770b67000 r--p 00000000 fe:00 1691 /system/lib64/libGLESv3.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 19 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 19 kB
+VmFlags: rd mr mw me
+700770b67000-700770b6e000 r-xp 00013000 fe:00 1691 /system/lib64/libGLESv3.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770b6e000-700770b6f000 rw-p 0001a000 fe:00 1691 /system/lib64/libGLESv3.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b6f000-700770b70000 r--p 0001b000 fe:00 1691 /system/lib64/libGLESv3.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770b71000-700770b72000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700770b72000-700770b76000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770b76000-700770b77000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770b77000-700770b79000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+700770b79000-700770b7b000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770b7b000-700770b7c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770b7c000-700770b7f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770b7f000-700770b80000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770b80000-700770baa000 r--p 00000000 fe:00 1605 /system/lib64/android.hardware.drm@1.0.so
+Size: 168 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700770baa000-700770bf8000 r-xp 0002a000 fe:00 1605 /system/lib64/android.hardware.drm@1.0.so
+Size: 312 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770bf8000-700770bf9000 rw-p 00078000 fe:00 1605 /system/lib64/android.hardware.drm@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770bf9000-700770c00000 r--p 00079000 fe:00 1605 /system/lib64/android.hardware.drm@1.0.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 28 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770c00000-700770c01000 rw-s 00000000 00:05 19682 /dev/ashmem/GFXStats-2254 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr sh mr mw me ms
+700770c01000-700770c02000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c02000-700770c05000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c05000-700770c06000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c06000-700770c15000 r--p 00000000 fe:00 1658 /system/lib64/libcamera2ndk.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 19 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 19 kB
+VmFlags: rd mr mw me
+700770c15000-700770c28000 r-xp 0000f000 fe:00 1658 /system/lib64/libcamera2ndk.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770c28000-700770c29000 rw-p 00022000 fe:00 1658 /system/lib64/libcamera2ndk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c29000-700770c2b000 r--p 00023000 fe:00 1658 /system/lib64/libcamera2ndk.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770c2b000-700770c2c000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c2c000-700770c2d000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700770c2d000-700770c2f000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770c2f000-700770c30000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c30000-700770c33000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c33000-700770c35000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c35000-700770c38000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c38000-700770c39000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c39000-700770c3a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770c3a000-700770c42000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c42000-700770c43000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c43000-700770c46000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c46000-700770c47000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c47000-700770c48000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770c48000-700770c50000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c50000-700770c51000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c51000-700770c54000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c54000-700770c55000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770c55000-700770c67000 r--p 00000000 fe:00 1269 /system/lib64/libmediadrmmetrics_lite.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700770c67000-700770c79000 r-xp 00012000 fe:00 1269 /system/lib64/libmediadrmmetrics_lite.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770c79000-700770c7a000 rw-p 00024000 fe:00 1269 /system/lib64/libmediadrmmetrics_lite.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c7a000-700770c7c000 r--p 00025000 fe:00 1269 /system/lib64/libmediadrmmetrics_lite.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770c7c000-700770c7d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c7d000-700770c7f000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770c7f000-700770c80000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770c80000-700770c88000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770c88000-700770c9c000 r--p 00000000 fe:00 1173 /system/lib64/libexif.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 15 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd mr mw me
+700770c9c000-700770cac000 r-xp 00014000 fe:00 1173 /system/lib64/libexif.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770cac000-700770cad000 rw-p 00024000 fe:00 1173 /system/lib64/libexif.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770cad000-700770cc0000 r--p 00025000 fe:00 1173 /system/lib64/libexif.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 76 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 76 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 76 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700770cc0000-700770cc2000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770cc2000-700770cd8000 r--p 00000000 fe:00 1584 /system/lib64/libmtp.so
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700770cd8000-700770cf0000 r-xp 00016000 fe:00 1584 /system/lib64/libmtp.so
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 88 kB
+Pss: 58 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 28 kB
+Private_Dirty: 0 kB
+Referenced: 88 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 58 kB
+VmFlags: rd ex mr mw me
+700770cf0000-700770cf1000 rw-p 0002e000 fe:00 1584 /system/lib64/libmtp.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770cf1000-700770cf4000 r--p 0002f000 fe:00 1584 /system/lib64/libmtp.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770cf4000-700770cf5000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770cf5000-700770cf9000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770cf9000-700770cfa000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770cfa000-700770cfd000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770cfd000-700770cfe000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770cfe000-700770cff000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770cff000-700770d07000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770d07000-700770d08000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770d08000-700770d10000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770d10000-700770d11000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700770d11000-700770d13000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770d13000-700770d14000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770d14000-700770d17000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770d17000-700770d18000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770d18000-700770d38000 r--s 00000000 00:13 6730 /dev/__properties__/u:object_r:pm_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms
+700770d38000-700770d39000 r--p 00000000 fe:00 1731 /system/lib64/libasyncio.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+700770d39000-700770d3a000 r-xp 00001000 fe:00 1731 /system/lib64/libasyncio.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770d3a000-700770d3b000 rw-p 00002000 fe:00 1731 /system/lib64/libasyncio.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770d3b000-700770d3c000 r--p 00003000 fe:00 1731 /system/lib64/libasyncio.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770d3d000-700770d3f000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770d3f000-700770d40000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770d40000-700770d43000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770d43000-700770d44000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770d44000-700770d77000 r--p 00000000 fe:00 1573 /system/lib64/libmedia_jni.so
+Size: 204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 15 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd mr mw me
+700770d77000-700770dae000 r-xp 00033000 fe:00 1573 /system/lib64/libmedia_jni.so
+Size: 220 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770dae000-700770daf000 rw-p 0006a000 fe:00 1573 /system/lib64/libmedia_jni.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770daf000-700770db5000 r--p 0006b000 fe:00 1573 /system/lib64/libmedia_jni.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770db5000-700770db6000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770db6000-700770db7000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770db7000-700770dbf000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dbf000-700770dc0000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770dc0000-700770dc3000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dc3000-700770dc4000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770dc4000-700770dc5000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770dc5000-700770dcd000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dcd000-700770dce000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770dce000-700770dd1000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dd1000-700770dd2000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770dd2000-700770dd3000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770dd3000-700770ddb000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770ddb000-700770ddc000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770ddc000-700770de4000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770de4000-700770ded000 r--p 00000000 fe:00 1558 /system/lib64/libmidi.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 9 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+700770ded000-700770df7000 r-xp 00009000 fe:00 1558 /system/lib64/libmidi.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770df7000-700770df8000 rw-p 00013000 fe:00 1558 /system/lib64/libmidi.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770df8000-700770dfa000 r--p 00014000 fe:00 1558 /system/lib64/libmidi.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770dfa000-700770dfb000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dfb000-700770dfc000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770dfc000-700770dff000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770dff000-700770e00000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770e00000-700770e1a000 r--p 00000000 fe:00 1744 /system/lib64/libmediadrm.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700770e1a000-700770e32000 r-xp 0001a000 fe:00 1744 /system/lib64/libmediadrm.so
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770e32000-700770e33000 rw-p 00032000 fe:00 1744 /system/lib64/libmediadrm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e33000-700770e3a000 r--p 00033000 fe:00 1744 /system/lib64/libmediadrm.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 28 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770e3a000-700770e3b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e3b000-700770e3c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770e3c000-700770e3f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e3f000-700770e40000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770e40000-700770e41000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770e41000-700770e49000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e49000-700770e59000 r--p 00000000 fe:00 1239 /system/lib64/libmediandk.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 14 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 14 kB
+VmFlags: rd mr mw me
+700770e59000-700770e66000 r-xp 00010000 fe:00 1239 /system/lib64/libmediandk.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 26 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 26 kB
+VmFlags: rd ex mr mw me
+700770e66000-700770e67000 rw-p 0001d000 fe:00 1239 /system/lib64/libmediandk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e67000-700770e69000 r--p 0001e000 fe:00 1239 /system/lib64/libmediandk.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770e69000-700770e6a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e6a000-700770e6e000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700770e6e000-700770e6f000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770e6f000-700770e77000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770e77000-700770e97000 r--s 00000000 00:13 6713 /dev/__properties__/u:object_r:hwservicemanager_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700770e97000-700770eb7000 r--p 00000000 fe:00 1647 /system/lib64/android.hardware.drm@1.1.so
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+700770eb7000-700770ee3000 r-xp 00020000 fe:00 1647 /system/lib64/android.hardware.drm@1.1.so
+Size: 176 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770ee3000-700770ee4000 rw-p 0004c000 fe:00 1647 /system/lib64/android.hardware.drm@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770ee4000-700770eea000 r--p 0004d000 fe:00 1647 /system/lib64/android.hardware.drm@1.1.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770eea000-700770eeb000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700770eeb000-700770eed000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770eed000-700770eee000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770eee000-700770ef1000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770ef1000-700770ef2000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770ef2000-700770ef3000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770ef3000-700770efb000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770efb000-700770f1b000 r--s 00000000 00:13 6745 /dev/__properties__/u:object_r:vendor_default_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700770f1b000-700770f22000 r--p 00000000 fe:00 1701 /system/lib64/libbinder_ndk.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 9 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+700770f22000-700770f28000 r-xp 00007000 fe:00 1701 /system/lib64/libbinder_ndk.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770f28000-700770f29000 rw-p 0000d000 fe:00 1701 /system/lib64/libbinder_ndk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f29000-700770f2a000 r--p 0000e000 fe:00 1701 /system/lib64/libbinder_ndk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770f2a000-700770f2b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f2b000-700770f2c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770f2c000-700770f2f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f2f000-700770f31000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770f31000-700770f34000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f34000-700770f35000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770f35000-700770f36000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770f36000-700770f3e000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f3e000-700770f3f000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770f3f000-700770f47000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f47000-700770f62000 r--p 00000000 fe:00 1703 /system/lib64/libaaudio.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 21 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+700770f62000-700770f74000 r-xp 0001b000 fe:00 1703 /system/lib64/libaaudio.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770f74000-700770f75000 rw-p 0002d000 fe:00 1703 /system/lib64/libaaudio.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f75000-700770f7a000 r--p 0002e000 fe:00 1703 /system/lib64/libaaudio.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+700770f7a000-700770f7b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770f7b000-700770f7c000 rw-s 00000000 00:05 11676 /dev/ashmem/GFXStats-1674 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr sh mr mw me ms
+700770f7c000-700770fa0000 r--s 00000000 fe:00 985 /system/usr/hyphen-data/hyph-nn.hyb
+Size: 144 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700770fa0000-700770faf000 r--p 00000000 fe:00 1602 /system/lib64/libandroid.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 15 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd mr mw me
+700770faf000-700770fb8000 r-xp 0000f000 fe:00 1602 /system/lib64/libandroid.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700770fb8000-700770fb9000 rw-p 00018000 fe:00 1602 /system/lib64/libandroid.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fb9000-700770fbb000 r--p 00019000 fe:00 1602 /system/lib64/libandroid.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770fbb000-700770fbc000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fbc000-700770fc0000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700770fc0000-700770fc1000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700770fc1000-700770fc9000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fc9000-700770fce000 r--p 00000000 fe:00 1736 /system/lib64/libadbconnection.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700770fce000-700770fd6000 r-xp 00005000 fe:00 1736 /system/lib64/libadbconnection.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 1 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+700770fd6000-700770fd7000 rw-p 0000d000 fe:00 1736 /system/lib64/libadbconnection.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fd7000-700770fd8000 r--p 0000e000 fe:00 1736 /system/lib64/libadbconnection.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700770fd8000-700770fd9000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fd9000-700770fda000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770fda000-700770fdd000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700770fdd000-700770fde000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700770fde000-70077102f000 r--s 00000000 fe:00 231 /system/fonts/Roboto-BlackItalic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+70077102f000-7007711bf000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 1600 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 24 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me ac
+7007711bf000-7007712bf000 rw-p 00000000 00:00 0 [anon:dalvik-non-moving-space inter region ref bitmap]
+Name: [anon:dalvik-non-moving-space inter region ref bitmap]
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007712bf000-7007722bf000 rw-p 00000000 00:00 0 [anon:dalvik-region-space inter region ref bitmap]
+Name: [anon:dalvik-region-space inter region ref bitmap]
+Size: 16384 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007722bf000-7007724bf000 rw-p 00000000 00:00 0 [anon:dalvik-rb copying gc mark stack]
+Name: [anon:dalvik-rb copying gc mark stack]
+Size: 2048 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007724bf000-700772cbf000 rw-p 00000000 00:00 0 [anon:dalvik-concurrent copying gc mark stack]
+Name: [anon:dalvik-concurrent copying gc mark stack]
+Size: 8192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700772cbf000-700772ebf000 rw-p 00000000 00:00 0 [anon:dalvik-rb copying gc mark stack]
+Name: [anon:dalvik-rb copying gc mark stack]
+Size: 2048 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700772ebf000-7007736bf000 rw-p 00000000 00:00 0 [anon:dalvik-concurrent copying gc mark stack]
+Name: [anon:dalvik-concurrent copying gc mark stack]
+Size: 8192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007736bf000-700773ec0000 rw-p 00000000 00:00 0 [anon:dalvik-live stack]
+Name: [anon:dalvik-live stack]
+Size: 8196 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700773ec0000-7007746c1000 rw-p 00000000 00:00 0 [anon:dalvik-allocation stack]
+Name: [anon:dalvik-allocation stack]
+Size: 8196 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007746c1000-700774ac2000 rw-p 00000000 00:00 0 [anon:dalvik-card table]
+Name: [anon:dalvik-card table]
+Size: 4100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 56 kB
+Pss: 56 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 56 kB
+Referenced: 56 kB
+Anonymous: 56 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 56 kB
+VmFlags: rd wr mr mw me ac
+700774ac2000-700775ac2000 rw-p 00000000 00:00 0 [anon:dalvik-region space live bitmap]
+Name: [anon:dalvik-region space live bitmap]
+Size: 16384 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 84 kB
+Pss: 84 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 84 kB
+Referenced: 84 kB
+Anonymous: 84 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 84 kB
+VmFlags: rd wr mr mw me ac
+700775ac2000-700775bc2000 rw-p 00000000 00:00 0 [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+Name: [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700775bc2000-700775cc2000 rw-p 00000000 00:00 0 [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+Name: [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
+Size: 1024 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd wr mr mw me ac
+700775cc2000-700775cc4000 r--p 00000000 fe:00 1730 /system/lib64/libsigchain.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700775cc4000-700775cc6000 r-xp 00002000 fe:00 1730 /system/lib64/libsigchain.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700775cc6000-700775cc7000 rw-p 00004000 fe:00 1730 /system/lib64/libsigchain.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700775cc7000-700775cc8000 r--p 00005000 fe:00 1730 /system/lib64/libsigchain.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700775cc8000-700775ccc000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700775ccc000-700775ccd000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700775ccd000-700775cd5000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700775cd5000-700775d26000 r--s 00000000 fe:00 219 /system/fonts/Roboto-MediumItalic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700775d26000-700775d27000 r--p 00000000 fe:00 1509 /system/lib64/libstatssocket.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700775d27000-700775d28000 r-xp 00001000 fe:00 1509 /system/lib64/libstatssocket.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700775d28000-700775d29000 rw-p 00002000 fe:00 1509 /system/lib64/libstatssocket.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700775d29000-700775d2a000 r--p 00003000 fe:00 1509 /system/lib64/libstatssocket.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700775d2a000-700775d2b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700775d2b000-700775d2f000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700775d2f000-700775d7a000 r--s 00000000 fe:00 275 /system/fonts/Roboto-Bold.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700775d7a000-700775ece000 r--p 00000000 fe:00 1156 /system/lib64/libart.so
+Size: 1360 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 384 kB
+Pss: 39 kB
+Shared_Clean: 384 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 384 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 39 kB
+VmFlags: rd mr mw me
+700775ece000-7007763e6000 r-xp 00154000 fe:00 1156 /system/lib64/libart.so
+Size: 5216 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4276 kB
+Pss: 288 kB
+Shared_Clean: 4272 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4276 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 288 kB
+VmFlags: rd ex mr mw me
+7007763e6000-7007763e9000 rw-p 0066c000 fe:00 1156 /system/lib64/libart.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 8 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007763e9000-7007763f9000 r--p 0066f000 fe:00 1156 /system/lib64/libart.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 64 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 56 kB
+Anonymous: 64 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007763f9000-7007763fc000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd wr mr mw me ac
+7007763fc000-700776400000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700776400000-700776800000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 4096 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4080 kB
+Pss: 3230 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 900 kB
+Private_Clean: 0 kB
+Private_Dirty: 3180 kB
+Referenced: 3992 kB
+Anonymous: 4080 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3230 kB
+VmFlags: rd wr mr mw me ac
+700776800000-700776804000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+700776804000-700776805000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+700776805000-700776807000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700776807000-700776808000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+700776808000-700776810000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776810000-700776861000 r--s 00000000 fe:00 195 /system/fonts/Roboto-Italic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700776861000-700776862000 r--p 00000000 fe:00 1199 /system/lib64/libmetricslogger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+700776862000-700776863000 r-xp 00001000 fe:00 1199 /system/lib64/libmetricslogger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700776863000-700776864000 rw-p 00002000 fe:00 1199 /system/lib64/libmetricslogger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776864000-700776865000 r--p 00003000 fe:00 1199 /system/lib64/libmetricslogger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700776865000-700776866000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776866000-700776867000 r--s 00009000 fe:00 2036 /system/priv-app/FusedLocation/FusedLocation.apk
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr me ms
+700776867000-700776868000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700776868000-70077686b000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+70077686b000-70077686c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70077686c000-700776890000 r--s 00000000 fe:00 1019 /system/usr/hyphen-data/hyph-nb.hyb
+Size: 144 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+700776890000-700776892000 r--p 00000000 fe:00 1606 /system/lib64/libtombstoned_client.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+700776892000-700776895000 r-xp 00002000 fe:00 1606 /system/lib64/libtombstoned_client.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+700776895000-700776896000 rw-p 00005000 fe:00 1606 /system/lib64/libtombstoned_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776896000-700776897000 r--p 00006000 fe:00 1606 /system/lib64/libtombstoned_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+700776897000-700776899000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+700776899000-70077689a000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70077689a000-70077689d000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+70077689d000-70077689f000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+70077689f000-7007768a2000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007768a2000-7007768a3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007768a3000-7007768c1000 r--s 00000000 fe:00 971 /system/usr/hyphen-data/hyph-de-ch-1901.hyb
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007768c1000-7007768d2000 r--p 00000000 fe:00 1651 /system/lib64/libprofile.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007768d2000-7007768f3000 r-xp 00011000 fe:00 1651 /system/lib64/libprofile.so
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007768f3000-7007768f4000 rw-p 00032000 fe:00 1651 /system/lib64/libprofile.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007768f4000-7007768f5000 r--p 00033000 fe:00 1651 /system/lib64/libprofile.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007768f5000-7007768f6000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007768f6000-7007768f8000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007768f8000-7007768f9000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007768f9000-700776901000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776901000-700776902000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700776902000-700776905000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+700776905000-700776906000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+700776906000-700776909000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+700776909000-70077690a000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+70077690a000-7007a2575000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 717228 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+7007a2575000-7007a2576000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007a2576000-7007ae940000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 200488 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+7007ae940000-7007ae941000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007ae941000-7007ae942000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007ae942000-7007ae945000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+7007ae945000-7007ae946000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007ae946000-7007b68f5000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 130748 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+7007b68f5000-7007b68f6000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007b68f6000-7007f6906000 r--p 00000000 00:00 0 [anon:cfi shadow]
+Name: [anon:cfi shadow]
+Size: 1048640 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me nr
+7007f6906000-7007f690d000 r--p 00000000 fe:00 1707 /system/lib64/libmediautils.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 4 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007f690d000-7007f6912000 r-xp 00007000 fe:00 1707 /system/lib64/libmediautils.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6912000-7007f6913000 rw-p 0000c000 fe:00 1707 /system/lib64/libmediautils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6913000-7007f6915000 r--p 0000d000 fe:00 1707 /system/lib64/libmediautils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6915000-7007f6916000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6916000-7007f6917000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6917000-7007f6919000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6919000-7007f691a000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f691a000-7007f6922000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6922000-7007f6940000 r--s 00000000 fe:00 976 /system/usr/hyphen-data/hyph-de-1996.hyb
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6940000-7007f694d000 r--p 00000000 fe:00 1526 /system/lib64/libmedia_helper.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 8 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007f694d000-7007f6955000 r-xp 0000d000 fe:00 1526 /system/lib64/libmedia_helper.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6955000-7007f6956000 rw-p 00015000 fe:00 1526 /system/lib64/libmedia_helper.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6956000-7007f6958000 r--p 00016000 fe:00 1526 /system/lib64/libmedia_helper.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6958000-7007f6959000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6959000-7007f695a000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f695a000-7007f695c000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f695c000-7007f695d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f695d000-7007f6960000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6960000-7007f6961000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6961000-7007f6963000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6963000-7007f6964000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6964000-7007f696c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f696c000-7007f696d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f696d000-7007f6970000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f6970000-7007f6971000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6971000-7007f698f000 r--s 00000000 fe:00 991 /system/usr/hyphen-data/hyph-de-1901.hyb
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f698f000-7007f699d000 r--p 00000000 fe:00 1227 /system/lib64/libminikin.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 8 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007f699d000-7007f69b5000 r-xp 0000e000 fe:00 1227 /system/lib64/libminikin.so
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 96 kB
+Pss: 24 kB
+Shared_Clean: 96 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 96 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd ex mr mw me
+7007f69b5000-7007f69b6000 rw-p 00026000 fe:00 1227 /system/lib64/libminikin.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69b6000-7007f69b7000 r--p 00027000 fe:00 1227 /system/lib64/libminikin.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f69b7000-7007f69b8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f69b8000-7007f69ba000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f69ba000-7007f69c1000 rw-p 00000000 fe:00 944 /system/etc/event-log-tags
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 2 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr mr mw me ac
+7007f69c1000-7007f69c2000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f69c2000-7007f69ca000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69ca000-7007f69d4000 r--p 00000000 fe:00 1135 /system/lib64/android.hidl.memory.token@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 7 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f69d4000-7007f69dd000 r-xp 0000a000 fe:00 1135 /system/lib64/android.hidl.memory.token@1.0.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f69dd000-7007f69de000 rw-p 00013000 fe:00 1135 /system/lib64/android.hidl.memory.token@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69de000-7007f69e0000 r--p 00014000 fe:00 1135 /system/lib64/android.hidl.memory.token@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f69e0000-7007f69e1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f69e1000-7007f69e4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69e4000-7007f69e6000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f69e6000-7007f69e9000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69e9000-7007f69ea000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f69ea000-7007f69eb000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f69eb000-7007f69f3000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69f3000-7007f69f4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f69f4000-7007f69fc000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f69fc000-7007f6a1c000 r--s 00000000 00:13 6716 /dev/__properties__/u:object_r:log_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6a1c000-7007f6a1d000 r--p 00000000 fe:00 1746 /system/lib64/android.hardware.configstore-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f6a1d000-7007f6a1e000 r-xp 00001000 fe:00 1746 /system/lib64/android.hardware.configstore-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+7007f6a1e000-7007f6a1f000 rw-p 00002000 fe:00 1746 /system/lib64/android.hardware.configstore-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a1f000-7007f6a20000 r--p 00003000 fe:00 1746 /system/lib64/android.hardware.configstore-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6a20000-7007f6a21000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6a21000-7007f6a24000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a24000-7007f6a25000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6a25000-7007f6a45000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a45000-7007f6a56000 r--p 00000000 fe:00 1599 /system/lib64/libvulkan.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 12 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+7007f6a56000-7007f6a69000 r-xp 00011000 fe:00 1599 /system/lib64/libvulkan.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6a69000-7007f6a6a000 rw-p 00024000 fe:00 1599 /system/lib64/libvulkan.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a6a000-7007f6a6c000 r--p 00025000 fe:00 1599 /system/lib64/libvulkan.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6a6c000-7007f6a6d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a6d000-7007f6a8d000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6a8d000-7007f6ab4000 r--p 00000000 fe:00 1727 /system/lib64/libandroidicu.so
+Size: 156 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 16 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd mr mw me
+7007f6ab4000-7007f6abc000 r-xp 00027000 fe:00 1727 /system/lib64/libandroidicu.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6abc000-7007f6abd000 rw-p 0002f000 fe:00 1727 /system/lib64/libandroidicu.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6abd000-7007f6abf000 r--p 00030000 fe:00 1727 /system/lib64/libandroidicu.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6abf000-7007f6ac3000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ac3000-7007f6ac4000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6ac4000-7007f6acc000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6acc000-7007f6acd000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6acd000-7007f6ad0000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ad0000-7007f6ad1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6ad1000-7007f6ad2000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6ad2000-7007f6ada000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ada000-7007f6ae9000 r--p 00000000 fe:00 1717 /system/lib64/android.hardware.configstore@1.1.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 5 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f6ae9000-7007f6af7000 r-xp 0000f000 fe:00 1717 /system/lib64/android.hardware.configstore@1.1.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6af7000-7007f6af8000 rw-p 0001d000 fe:00 1717 /system/lib64/android.hardware.configstore@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6af8000-7007f6afb000 r--p 0001e000 fe:00 1717 /system/lib64/android.hardware.configstore@1.1.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6afb000-7007f6afc000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f6afc000-7007f6afe000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6afe000-7007f6aff000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6aff000-7007f6b02000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6b02000-7007f6b04000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6b04000-7007f6b07000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6b07000-7007f6b08000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6b08000-7007f6b09000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6b09000-7007f6b11000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6b11000-7007f6b62000 r--p 00000000 fe:00 1580 /system/lib64/libbinder.so
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 252 kB
+Pss: 9 kB
+Shared_Clean: 252 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 252 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f6b62000-7007f6ba1000 r-xp 00051000 fe:00 1580 /system/lib64/libbinder.so
+Size: 252 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 248 kB
+Pss: 6 kB
+Shared_Clean: 248 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 248 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007f6ba1000-7007f6ba2000 rw-p 00090000 fe:00 1580 /system/lib64/libbinder.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ba2000-7007f6bb1000 r--p 00091000 fe:00 1580 /system/lib64/libbinder.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 60 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 60 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007f6bb1000-7007f6bb2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6bb2000-7007f6bb3000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6bb3000-7007f6bb6000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6bb6000-7007f6bb7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6bb7000-7007f6bb8000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6bb8000-7007f6bc0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6bc0000-7007f6bc1000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6bc1000-7007f6bc9000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6bc9000-7007f6bdf000 r--p 00000000 fe:00 1694 /system/lib64/libinput.so
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 88 kB
+Pss: 20 kB
+Shared_Clean: 88 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 88 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+7007f6bdf000-7007f6bf3000 r-xp 00016000 fe:00 1694 /system/lib64/libinput.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 18 kB
+Shared_Clean: 72 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 18 kB
+VmFlags: rd ex mr mw me
+7007f6bf3000-7007f6bf4000 rw-p 0002a000 fe:00 1694 /system/lib64/libinput.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6bf4000-7007f6bfb000 r--p 0002b000 fe:00 1694 /system/lib64/libinput.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 28 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f6bfb000-7007f6bfc000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6bfc000-7007f6bfd000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007f6bfd000-7007f6c05000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6c05000-7007f6c0e000 r--s 00000000 fe:00 972 /system/usr/hyphen-data/hyph-ga.hyb
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6c0e000-7007f6c1a000 r--p 00000000 fe:00 1613 /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 4 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007f6c1a000-7007f6c26000 r-xp 0000c000 fe:00 1613 /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 6 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007f6c26000-7007f6c27000 rw-p 00018000 fe:00 1613 /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6c27000-7007f6c29000 r--p 00019000 fe:00 1613 /system/lib64/android.hardware.graphics.mapper@2.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6c29000-7007f6c2b000 r--p 0007b000 fe:00 3288 /system/framework/oat/x86_64/services.art
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007f6c2b000-7007f6c3a000 r--s 00000000 fe:00 984 /system/usr/hyphen-data/hyph-en-us.hyb
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 30 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 30 kB
+VmFlags: rd mr me ms
+7007f6c3a000-7007f6c46000 r--s 00000000 fe:00 1020 /system/usr/hyphen-data/hyph-en-gb.hyb
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6c46000-7007f6c5b000 r--p 00000000 fe:00 1210 /system/lib64/libstagefright_foundation.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007f6c5b000-7007f6c7a000 r-xp 00015000 fe:00 1210 /system/lib64/libstagefright_foundation.so
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 27 kB
+Shared_Clean: 124 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 124 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 27 kB
+VmFlags: rd ex mr mw me
+7007f6c7a000-7007f6c7b000 rw-p 00034000 fe:00 1210 /system/lib64/libstagefright_foundation.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6c7b000-7007f6c7d000 r--p 00035000 fe:00 1210 /system/lib64/libstagefright_foundation.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6c7d000-7007f6c7e000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6c7e000-7007f6c7f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f6c7f000-7007f6c81000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6c81000-7007f6ca1000 rw-p 00000000 00:00 0 [anon:dalvik-CompilerMetadata]
+Name: [anon:dalvik-CompilerMetadata]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ca1000-7007f6ca3000 r--p 00000000 fe:00 1711 /system/lib64/libhardware_legacy.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 1 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f6ca3000-7007f6ca5000 r-xp 00002000 fe:00 1711 /system/lib64/libhardware_legacy.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd ex mr mw me
+7007f6ca5000-7007f6ca6000 rw-p 00004000 fe:00 1711 /system/lib64/libhardware_legacy.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ca6000-7007f6ca7000 r--p 00005000 fe:00 1711 /system/lib64/libhardware_legacy.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6ca7000-7007f6ca8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ca8000-7007f6ca9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6ca9000-7007f6cac000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6cac000-7007f6cad000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007f6cad000-7007f6cba000 r--s 00000000 fe:00 1022 /system/usr/hyphen-data/hyph-cu.hyb
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6cba000-7007f6cda000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 128 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 128 kB
+Referenced: 128 kB
+Anonymous: 128 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 128 kB
+VmFlags: rd wr mr mw me ac
+7007f6cda000-7007f6cdf000 r--p 00000000 fe:00 1678 /system/lib64/libappfuse.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 5 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f6cdf000-7007f6ce6000 r-xp 00005000 fe:00 1678 /system/lib64/libappfuse.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6ce6000-7007f6ce7000 rw-p 0000c000 fe:00 1678 /system/lib64/libappfuse.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ce7000-7007f6ce8000 r--p 0000d000 fe:00 1678 /system/lib64/libappfuse.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6ce8000-7007f6cea000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6cea000-7007f6cf3000 r--s 00000000 fe:00 1000 /system/usr/hyphen-data/hyph-cy.hyb
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6cf3000-7007f6d0c000 r--s 00000000 fe:00 162 /system/fonts/NotoSansBhaiksuki-Regular.otf
+Size: 100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6d0c000-7007f6d18000 r--p 00000000 fe:00 1607 /system/lib64/android.hardware.cas.native@1.0.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 9 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f6d18000-7007f6d23000 r-xp 0000c000 fe:00 1607 /system/lib64/android.hardware.cas.native@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6d23000-7007f6d24000 rw-p 00017000 fe:00 1607 /system/lib64/android.hardware.cas.native@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6d24000-7007f6d26000 r--p 00018000 fe:00 1607 /system/lib64/android.hardware.cas.native@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6d26000-7007f6d2c000 r--s 00000000 fe:00 1025 /system/usr/hyphen-data/hyph-et.hyb
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6d2c000-7007f6d3d000 r--s 00000000 fe:00 274 /system/fonts/NotoSansNewa-Regular.otf
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6d3d000-7007f6d5e000 r--s 00000000 fe:00 142 /system/fonts/NotoSansAnatolianHieroglyphs-Regular.otf
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6d5e000-7007f6d60000 r--p 00000000 fe:00 1650 /system/lib64/libstagefright_omx_utils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 1 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f6d60000-7007f6d62000 r-xp 00002000 fe:00 1650 /system/lib64/libstagefright_omx_utils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 4 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+7007f6d62000-7007f6d63000 rw-p 00004000 fe:00 1650 /system/lib64/libstagefright_omx_utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6d63000-7007f6d64000 r--p 00005000 fe:00 1650 /system/lib64/libstagefright_omx_utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6d64000-7007f6d65000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6d65000-7007f6d67000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6d67000-7007f6db2000 r--s 00000000 fe:00 144 /system/fonts/Roboto-Black.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6db2000-7007f6db5000 r--p 00000000 fe:00 1142 /system/lib64/libhidlmemory.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 2 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007f6db5000-7007f6db7000 r-xp 00003000 fe:00 1142 /system/lib64/libhidlmemory.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+7007f6db7000-7007f6db8000 rw-p 00005000 fe:00 1142 /system/lib64/libhidlmemory.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6db8000-7007f6db9000 r--p 00006000 fe:00 1142 /system/lib64/libhidlmemory.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6db9000-7007f6dba000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6dba000-7007f6dca000 r--s 00000000 fe:00 119 /system/fonts/NotoSansMarchen-Regular.otf
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6dca000-7007f6dcc000 r--p 00000000 fe:00 1469 /system/lib64/libprocessgroup.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f6dcc000-7007f6dcf000 r-xp 00002000 fe:00 1469 /system/lib64/libprocessgroup.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6dcf000-7007f6dd0000 rw-p 00005000 fe:00 1469 /system/lib64/libprocessgroup.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6dd0000-7007f6dd1000 r--p 00006000 fe:00 1469 /system/lib64/libprocessgroup.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6dd1000-7007f6dd2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6dd2000-7007f6dd3000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f6dd3000-7007f6dd7000 r--s 00000000 fe:00 1008 /system/usr/hyphen-data/hyph-es.hyb
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6dd7000-7007f6dde000 r--s 00000000 fe:00 132 /system/fonts/NotoSansSharada-Regular.otf
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6dde000-7007f6de7000 r--s 00000000 fe:00 157 /system/fonts/NotoSansLinearA-Regular.otf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6de7000-7007f6e03000 r--s 00000000 fe:00 139 /system/fonts/NotoSansMongolian-Regular.ttf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6e03000-7007f6e15000 r--p 00000000 fe:00 1511 /system/lib64/libpdx_default_transport.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 9 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f6e15000-7007f6e29000 r-xp 00012000 fe:00 1511 /system/lib64/libpdx_default_transport.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6e29000-7007f6e2a000 rw-p 00026000 fe:00 1511 /system/lib64/libpdx_default_transport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6e2a000-7007f6e2b000 r--p 00027000 fe:00 1511 /system/lib64/libpdx_default_transport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6e2b000-7007f6e2c000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6e2c000-7007f6e2d000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f6e2d000-7007f6e2f000 r--s 00000000 fe:00 964 /system/usr/hyphen-data/hyph-sl.hyb
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6e2f000-7007f6e4e000 r--s 00000000 fe:00 289 /system/fonts/NotoSansYi-Regular.ttf
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6e4e000-7007f6e60000 r--p 00000000 fe:00 1545 /system/lib64/libEGL.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 72 kB
+Pss: 8 kB
+Shared_Clean: 72 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007f6e60000-7007f6e73000 r-xp 00012000 fe:00 1545 /system/lib64/libEGL.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 76 kB
+Pss: 3 kB
+Shared_Clean: 76 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 76 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd ex mr mw me
+7007f6e73000-7007f6e74000 rw-p 00025000 fe:00 1545 /system/lib64/libEGL.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6e74000-7007f6e78000 r--p 00026000 fe:00 1545 /system/lib64/libEGL.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6e78000-7007f6e80000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 20 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me ac
+7007f6e80000-7007f6e82000 r--s 00000000 fe:00 983 /system/usr/hyphen-data/hyph-mn-cyrl.hyb
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6e82000-7007f6e91000 r--p 00000000 fe:00 1200 /system/lib64/libutils.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 1 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f6e91000-7007f6e9d000 r-xp 0000f000 fe:00 1200 /system/lib64/libutils.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 1 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+7007f6e9d000-7007f6e9e000 rw-p 0001b000 fe:00 1200 /system/lib64/libutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6e9e000-7007f6e9f000 r--p 0001c000 fe:00 1200 /system/lib64/libutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6e9f000-7007f6ea0000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ea1000-7007f6eb1000 r--s 00000000 fe:00 220 /system/fonts/NotoSansVai-Regular.ttf
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6eb1000-7007f6eca000 r--s 00000000 fe:00 154 /system/fonts/NotoSansLepcha-Regular.ttf
+Size: 100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6eca000-7007f6ecb000 r--p 00000000 fe:00 1636 /system/lib64/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f6ecb000-7007f6ecc000 r-xp 00001000 fe:00 1636 /system/lib64/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6ecc000-7007f6ecd000 rw-p 00002000 fe:00 1636 /system/lib64/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ecd000-7007f6ece000 r--p 00003000 fe:00 1636 /system/lib64/android.hardware.graphics.common@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6ece000-7007f6ed0000 r--s 00000000 fe:00 986 /system/usr/hyphen-data/hyph-fr.hyb
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6ed0000-7007f6edc000 r--s 00000000 fe:00 156 /system/fonts/NotoSansTaiTham-Regular.ttf
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6edc000-7007f6efd000 r--s 00000000 fe:00 147 /system/fonts/NotoSansBamum-Regular.ttf
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6efd000-7007f6f17000 r--s 00000000 fe:00 105 /system/fonts/NotoSansMyanmarUI-Bold.ttf
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6f17000-7007f6f54000 r--p 00000000 fe:00 1563 /system/lib64/libjpeg.so
+Size: 244 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 15 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15 kB
+VmFlags: rd mr mw me
+7007f6f54000-7007f6fa6000 r-xp 0003d000 fe:00 1563 /system/lib64/libjpeg.so
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6fa6000-7007f6fa7000 rw-p 0008f000 fe:00 1563 /system/lib64/libjpeg.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6fa7000-7007f6fa8000 r--p 00090000 fe:00 1563 /system/lib64/libjpeg.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6fa8000-7007f6fa9000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6fa9000-7007f6faa000 r-xp 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me ac
+7007f6faa000-7007f6ff5000 r--s 00000000 fe:00 121 /system/fonts/Roboto-Medium.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 148 kB
+Pss: 47 kB
+Shared_Clean: 148 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 148 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 47 kB
+VmFlags: rd mr me ms
+7007f6ff5000-7007f6ff6000 r--p 00000000 fe:00 1623 /system/lib64/libvndksupport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f6ff6000-7007f6ff7000 r-xp 00001000 fe:00 1623 /system/lib64/libvndksupport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f6ff7000-7007f6ff8000 rw-p 00002000 fe:00 1623 /system/lib64/libvndksupport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ff8000-7007f6ff9000 r--p 00003000 fe:00 1623 /system/lib64/libvndksupport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f6ff9000-7007f6ffa000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f6ffa000-7007f6ffb000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f6ffb000-7007f6ffd000 r--s 00000000 fe:00 1014 /system/usr/hyphen-data/hyph-da.hyb
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f6ffd000-7007f7001000 r--s 00000000 fe:00 199 /system/fonts/NotoSansPahawhHmong-Regular.otf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7001000-7007f700e000 r--s 00000000 fe:00 159 /system/fonts/NotoSansSyriacWestern-Regular.ttf
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f700e000-7007f7028000 r--s 00000000 fe:00 80 /system/fonts/NotoSansMyanmarUI-Regular.ttf
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7028000-7007f702b000 r--p 00000000 fe:00 1747 /system/lib64/libnetdbpf.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 2 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007f702b000-7007f7031000 r-xp 00003000 fe:00 1747 /system/lib64/libnetdbpf.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7031000-7007f7032000 rw-p 00009000 fe:00 1747 /system/lib64/libnetdbpf.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7032000-7007f7033000 r--p 0000a000 fe:00 1747 /system/lib64/libnetdbpf.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7033000-7007f7034000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7034000-7007f7037000 r--s 00000000 fe:00 214 /system/fonts/NotoSansPauCinHau-Regular.otf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7037000-7007f7044000 r--s 00000000 fe:00 194 /system/fonts/NotoSansSyriacEastern-Regular.ttf
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7044000-7007f704b000 r--p 00000000 fe:00 1620 /system/lib64/libmediametrics.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 3 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007f704b000-7007f7051000 r-xp 00007000 fe:00 1620 /system/lib64/libmediametrics.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 4 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+7007f7051000-7007f7052000 rw-p 0000d000 fe:00 1620 /system/lib64/libmediametrics.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7052000-7007f7054000 r--p 0000e000 fe:00 1620 /system/lib64/libmediametrics.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7054000-7007f7055000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f7055000-7007f7058000 r--s 00000000 fe:00 141 /system/fonts/NotoSansPalmyrene-Regular.otf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7058000-7007f707a000 r--s 00000000 fe:00 226 /system/fonts/NotoSerifMyanmar-Bold.otf
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f707a000-7007f709c000 r--s 00000000 fe:00 153 /system/fonts/NotoSerifMyanmar-Regular.otf
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f709c000-7007f70da000 r--p 00000000 fe:00 1512 /system/lib64/libaudioclient.so
+Size: 248 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 22 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 22 kB
+VmFlags: rd mr mw me
+7007f70da000-7007f7110000 r-xp 0003e000 fe:00 1512 /system/lib64/libaudioclient.so
+Size: 216 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 188 kB
+Pss: 41 kB
+Shared_Clean: 188 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 188 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 41 kB
+VmFlags: rd ex mr mw me
+7007f7110000-7007f7111000 rw-p 00074000 fe:00 1512 /system/lib64/libaudioclient.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7111000-7007f711f000 r--p 00075000 fe:00 1512 /system/lib64/libaudioclient.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 56 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 56 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 56 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007f711f000-7007f7120000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f7120000-7007f7126000 r--s 00000000 fe:00 108 /system/fonts/NotoSansMiao-Regular.otf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7126000-7007f7132000 r--s 00000000 fe:00 206 /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7132000-7007f714d000 r--s 00000000 fe:00 227 /system/fonts/NotoSansMyanmar-Bold.ttf
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f714d000-7007f7172000 r--p 00000000 fe:00 1732 /system/lib64/libmedia_omx.so
+Size: 148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 120 kB
+Pss: 24 kB
+Shared_Clean: 120 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 120 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr mw me
+7007f7172000-7007f7194000 r-xp 00025000 fe:00 1732 /system/lib64/libmedia_omx.so
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 136 kB
+Pss: 31 kB
+Shared_Clean: 136 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 136 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 31 kB
+VmFlags: rd ex mr mw me
+7007f7194000-7007f7195000 rw-p 00047000 fe:00 1732 /system/lib64/libmedia_omx.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7195000-7007f71a0000 r--p 00048000 fe:00 1732 /system/lib64/libmedia_omx.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 44 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 44 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me ac
+7007f71a0000-7007f71a1000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f71a1000-7007f71a3000 r--s 00000000 fe:00 997 /system/usr/hyphen-data/hyph-be.hyb
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f71a3000-7007f71be000 r--s 00000000 fe:00 187 /system/fonts/NotoSansMyanmar-Regular.ttf
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f71be000-7007f71e2000 r--s 00000000 fe:00 180 /system/fonts/NotoSansKhmer-VF.ttf
+Size: 144 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f71e2000-7007f71ea000 r--p 00000000 fe:00 1655 /system/lib64/libselinux.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 0 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f71ea000-7007f71fa000 r-xp 00008000 fe:00 1655 /system/lib64/libselinux.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 2 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+7007f71fa000-7007f71fb000 rw-p 00018000 fe:00 1655 /system/lib64/libselinux.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f71fb000-7007f71fc000 r--p 00019000 fe:00 1655 /system/lib64/libselinux.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f71fc000-7007f71fe000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f71fe000-7007f7203000 r--s 00000000 fe:00 261 /system/fonts/NotoSansMeroitic-Regular.otf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7203000-7007f7212000 r--s 00000000 fe:00 190 /system/fonts/NotoSansLinearB-Regular.ttf
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7212000-7007f7219000 r--p 00000000 fe:00 1697 /system/lib64/libpiex.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 6 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007f7219000-7007f722c000 r-xp 00007000 fe:00 1697 /system/lib64/libpiex.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f722c000-7007f722d000 rw-p 0001a000 fe:00 1697 /system/lib64/libpiex.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f722d000-7007f722e000 r--p 0001b000 fe:00 1697 /system/lib64/libpiex.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f722e000-7007f722f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f722f000-7007f7238000 r--s 00000000 fe:00 259 /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7238000-7007f724d000 r--s 00000000 fe:00 72 /system/fonts/NotoSansSinhalaUI-Bold.otf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f724d000-7007f7262000 r--s 00000000 fe:00 241 /system/fonts/NotoSansSinhalaUI-Regular.otf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7262000-7007f726a000 r--p 00000000 fe:00 1240 /system/lib64/libaudioutils.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 5 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f726a000-7007f7274000 r-xp 00008000 fe:00 1240 /system/lib64/libaudioutils.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7274000-7007f7275000 rw-p 00012000 fe:00 1240 /system/lib64/libaudioutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7275000-7007f7276000 r--p 00013000 fe:00 1240 /system/lib64/libaudioutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7276000-7007f7277000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f7277000-7007f7286000 r--s 00000000 fe:00 186 /system/fonts/NotoSansKaithi-Regular.ttf
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7286000-7007f7304000 r--p 00000000 fe:00 1125 /system/lib64/libcrypto.so
+Size: 504 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 296 kB
+Pss: 18 kB
+Shared_Clean: 296 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 296 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 18 kB
+VmFlags: rd mr mw me
+7007f7304000-7007f73e2000 r-xp 0007e000 fe:00 1125 /system/lib64/libcrypto.so
+Size: 888 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 752 kB
+Pss: 256 kB
+Shared_Clean: 632 kB
+Shared_Dirty: 0 kB
+Private_Clean: 120 kB
+Private_Dirty: 0 kB
+Referenced: 752 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 256 kB
+VmFlags: rd ex mr mw me
+7007f73e2000-7007f73e3000 rw-p 0015c000 fe:00 1125 /system/lib64/libcrypto.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f73e3000-7007f73f4000 r--p 0015d000 fe:00 1125 /system/lib64/libcrypto.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 68 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 68 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007f73f4000-7007f73f6000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f73f6000-7007f73fd000 r--s 00000000 fe:00 267 /system/fonts/NotoSansPhagsPa-Regular.ttf
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f73fd000-7007f7423000 r--s 00000000 fe:00 129 /system/fonts/NotoSansSinhala-Bold.ttf
+Size: 152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7423000-7007f7426000 r--p 00000000 fe:00 1569 /system/lib64/libnativewindow.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f7426000-7007f7428000 r-xp 00003000 fe:00 1569 /system/lib64/libnativewindow.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7428000-7007f7429000 rw-p 00005000 fe:00 1569 /system/lib64/libnativewindow.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7429000-7007f742a000 r--p 00006000 fe:00 1569 /system/lib64/libnativewindow.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f742a000-7007f7434000 r--s 00000000 fe:00 178 /system/fonts/NotoSansJavanese-Regular.ttf
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7434000-7007f7449000 r--s 00000000 fe:00 198 /system/fonts/NotoSerifSinhala-Bold.otf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7449000-7007f7471000 r--s 00000000 fe:00 115 /system/fonts/NotoSansSinhala-Regular.ttf
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7471000-7007f7473000 r--p 00000000 fe:00 1740 /system/lib64/android.hidl.token@1.0-utils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f7473000-7007f7474000 r-xp 00002000 fe:00 1740 /system/lib64/android.hidl.token@1.0-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7474000-7007f7475000 rw-p 00003000 fe:00 1740 /system/lib64/android.hidl.token@1.0-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7475000-7007f7476000 r--p 00004000 fe:00 1740 /system/lib64/android.hidl.token@1.0-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7476000-7007f7477000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7477000-7007f7486000 r--s 00000000 fe:00 76 /system/fonts/NotoSansCherokee-Regular.ttf
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7486000-7007f7499000 r--s 00000000 fe:00 131 /system/fonts/NotoSerifSinhala-Regular.otf
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7499000-7007f74ad000 r--s 00000000 fe:00 284 /system/fonts/NotoSansOriyaUI-Bold.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f74ad000-7007f74c9000 r--p 00000000 fe:00 1562 /system/lib64/android.hardware.cas@1.0.so
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 12 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+7007f74c9000-7007f74f5000 r-xp 0001c000 fe:00 1562 /system/lib64/android.hardware.cas@1.0.so
+Size: 176 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f74f5000-7007f74f6000 rw-p 00048000 fe:00 1562 /system/lib64/android.hardware.cas@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f74f6000-7007f74fb000 r--p 00049000 fe:00 1562 /system/lib64/android.hardware.cas@1.0.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f74fb000-7007f7500000 r--s 00000000 fe:00 86 /system/fonts/NotoSansManichaean-Regular.otf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7500000-7007f7514000 r--s 00000000 fe:00 244 /system/fonts/NotoSansOriyaUI-Regular.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7514000-7007f7523000 r--p 00000000 fe:00 1198 /system/lib64/libhidlbase.so
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 60 kB
+Pss: 2 kB
+Shared_Clean: 60 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 60 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007f7523000-7007f7536000 r-xp 0000f000 fe:00 1198 /system/lib64/libhidlbase.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 76 kB
+Pss: 3 kB
+Shared_Clean: 76 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 76 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd ex mr mw me
+7007f7536000-7007f7537000 rw-p 00022000 fe:00 1198 /system/lib64/libhidlbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7537000-7007f7539000 r--p 00023000 fe:00 1198 /system/lib64/libhidlbase.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7539000-7007f753a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f753a000-7007f753b000 rw-s 00000000 00:05 11403 /dev/ashmem/GFXStats-1521 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr sh mr mw me ms
+7007f753b000-7007f7547000 r--s 00000000 fe:00 283 /system/fonts/NotoSansCanadianAboriginal-Regular.ttf
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7547000-7007f7558000 r--p 00000000 fe:00 1748 /system/lib64/libpng.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007f7558000-7007f757e000 r-xp 00011000 fe:00 1748 /system/lib64/libpng.so
+Size: 152 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 152 kB
+Pss: 38 kB
+Shared_Clean: 152 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 152 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 38 kB
+VmFlags: rd ex mr mw me
+7007f757e000-7007f757f000 rw-p 00037000 fe:00 1748 /system/lib64/libpng.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f757f000-7007f7580000 r--p 00038000 fe:00 1748 /system/lib64/libpng.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7580000-7007f75a0000 r--p 00000000 fe:00 1539 /system/lib64/libpcre2.so
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 20 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd mr mw me
+7007f75a0000-7007f75cf000 r-xp 00020000 fe:00 1539 /system/lib64/libpcre2.so
+Size: 188 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 184 kB
+Pss: 37 kB
+Shared_Clean: 184 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 184 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 37 kB
+VmFlags: rd ex mr mw me
+7007f75cf000-7007f75d0000 rw-p 0004f000 fe:00 1539 /system/lib64/libpcre2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f75d0000-7007f75d1000 r--p 00050000 fe:00 1539 /system/lib64/libpcre2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f75d1000-7007f75d4000 r--s 00000000 fe:00 281 /system/fonts/NotoSansOldPermic-Regular.otf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f75d4000-7007f75d8000 r--s 00000000 fe:00 268 /system/fonts/NotoSansTifinagh-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f75d8000-7007f75ed000 r--s 00000000 fe:00 60 /system/fonts/NotoSansOriya-Bold.ttf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f75ed000-7007f7602000 r--s 00000000 fe:00 120 /system/fonts/NotoSansOriya-Regular.ttf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7602000-7007f760a000 r--p 00000000 fe:00 1141 /system/lib64/libnetdutils.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 6 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007f760a000-7007f7614000 r-xp 00008000 fe:00 1141 /system/lib64/libnetdutils.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7614000-7007f7615000 rw-p 00012000 fe:00 1141 /system/lib64/libnetdutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7615000-7007f7616000 r--p 00013000 fe:00 1141 /system/lib64/libnetdutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7616000-7007f7617000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7617000-7007f7619000 r--s 00000000 fe:00 277 /system/fonts/NotoSansSoraSompeng-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7619000-7007f761d000 r--s 00000000 fe:00 225 /system/fonts/NotoSansTaiViet-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f761d000-7007f7668000 r--s 00000000 fe:00 83 /system/fonts/Roboto-Regular.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 208 kB
+Pss: 55 kB
+Shared_Clean: 208 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 208 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 55 kB
+VmFlags: rd mr me ms
+7007f7668000-7007f7909000 r--p 00000000 fe:00 1705 /system/lib64/libpdfium.so
+Size: 2692 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 37 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 37 kB
+VmFlags: rd mr mw me
+7007f7909000-7007f7b59000 r-xp 002a1000 fe:00 1705 /system/lib64/libpdfium.so
+Size: 2368 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7b59000-7007f7b5d000 rw-p 004f1000 fe:00 1705 /system/lib64/libpdfium.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7b5d000-7007f7b72000 r--p 004f5000 fe:00 1705 /system/lib64/libpdfium.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 84 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 84 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 84 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f7b72000-7007f7b7a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7b7a000-7007f7b7c000 r--s 00000000 fe:00 230 /system/fonts/NotoSansOldNorthArabian-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7b7c000-7007f7b85000 r--s 00000000 fe:00 137 /system/fonts/NotoSansChakma-Regular.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7b85000-7007f7b9c000 r--s 00000000 fe:00 210 /system/fonts/NotoSerifKannada-Bold.ttf
+Size: 92 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7b9c000-7007f7bb3000 r--s 00000000 fe:00 160 /system/fonts/NotoSerifKannada-Regular.ttf
+Size: 92 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7bb3000-7007f7bcd000 r--p 00000000 fe:00 1595 /system/lib64/libwilhelm.so
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007f7bcd000-7007f7bed000 r-xp 0001a000 fe:00 1595 /system/lib64/libwilhelm.so
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7bed000-7007f7bee000 rw-p 0003a000 fe:00 1595 /system/lib64/libwilhelm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7bee000-7007f7bf3000 r--p 0003b000 fe:00 1595 /system/lib64/libwilhelm.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f7bf3000-7007f7bf4000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7bf4000-7007f7bf6000 r--s 00000000 fe:00 213 /system/fonts/NotoSansNabataean-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7bf6000-7007f7c0a000 r--s 00000000 fe:00 169 /system/fonts/NotoSansKannadaUI-Bold.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7c0a000-7007f7c96000 r--p 00000000 fe:00 1659 /system/lib64/libstagefright.so
+Size: 560 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 256 kB
+Pss: 66 kB
+Shared_Clean: 256 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 256 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 66 kB
+VmFlags: rd mr mw me
+7007f7c96000-7007f7dad000 r-xp 0008c000 fe:00 1659 /system/lib64/libstagefright.so
+Size: 1116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 788 kB
+Pss: 289 kB
+Shared_Clean: 788 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 788 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 289 kB
+VmFlags: rd ex mr mw me
+7007f7dad000-7007f7dae000 rw-p 001a3000 fe:00 1659 /system/lib64/libstagefright.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7dae000-7007f7dbe000 r--p 001a4000 fe:00 1659 /system/lib64/libstagefright.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 64 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 64 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007f7dbe000-7007f7dbf000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f7dbf000-7007f7ddb000 r--s 00000000 fe:00 291 /system/fonts/NotoSansTeluguUI-Bold.ttf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7ddb000-7007f7de5000 r--p 00000000 fe:00 1770 /system/lib64/android.hidl.allocator@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 6 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007f7de5000-7007f7def000 r-xp 0000a000 fe:00 1770 /system/lib64/android.hidl.allocator@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 7 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd ex mr mw me
+7007f7def000-7007f7df0000 rw-p 00014000 fe:00 1770 /system/lib64/android.hidl.allocator@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7df0000-7007f7df2000 r--p 00015000 fe:00 1770 /system/lib64/android.hidl.allocator@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7df2000-7007f7df3000 rw-s 00000000 00:05 11403 /dev/ashmem/GFXStats-1521 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr sh mr mw me ms
+7007f7df3000-7007f7e07000 r--s 00000000 fe:00 236 /system/fonts/NotoSansKannadaUI-Regular.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7e07000-7007f7e10000 r--p 00000000 fe:00 1203 /system/lib64/liblzma.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 3 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007f7e10000-7007f7e2f000 r-xp 00009000 fe:00 1203 /system/lib64/liblzma.so
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7e2f000-7007f7e30000 rw-p 00028000 fe:00 1203 /system/lib64/liblzma.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7e30000-7007f7e31000 r--p 00029000 fe:00 1203 /system/lib64/liblzma.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7e31000-7007f7e38000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7e38000-7007f7e3d000 r--s 00000000 fe:00 269 /system/fonts/NotoSansSaurashtra-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7e3d000-7007f7e45000 r--s 00000000 fe:00 204 /system/fonts/NotoSansBalinese-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7e45000-7007f7e50000 r--p 00000000 fe:00 1544 /system/lib64/android.hidl.token@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 5 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f7e50000-7007f7e5b000 r-xp 0000b000 fe:00 1544 /system/lib64/android.hidl.token@1.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7e5b000-7007f7e5c000 rw-p 00016000 fe:00 1544 /system/lib64/android.hidl.token@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7e5c000-7007f7e5e000 r--p 00017000 fe:00 1544 /system/lib64/android.hidl.token@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7e5e000-7007f7e60000 r--s 00000000 fe:00 85 /system/fonts/NotoSansMultani-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7e60000-7007f7e65000 r--s 00000000 fe:00 168 /system/fonts/NotoSansNKo-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7e65000-7007f7eb7000 r--s 00000000 fe:00 165 /system/fonts/Roboto-LightItalic.ttf
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7eb7000-7007f7ed5000 r--p 00000000 fe:00 1126 /system/lib64/libprotobuf-cpp-lite.so
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 96 kB
+Pss: 8 kB
+Shared_Clean: 96 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 96 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me
+7007f7ed5000-7007f7ef4000 r-xp 0001e000 fe:00 1126 /system/lib64/libprotobuf-cpp-lite.so
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 11 kB
+Shared_Clean: 124 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 124 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd ex mr mw me
+7007f7ef4000-7007f7ef5000 rw-p 0003d000 fe:00 1126 /system/lib64/libprotobuf-cpp-lite.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7ef5000-7007f7ef7000 r--p 0003e000 fe:00 1126 /system/lib64/libprotobuf-cpp-lite.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7ef7000-7007f7ef8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7ef8000-7007f7ef9000 r--p 00000000 00:00 0 [anon:atexit handlers]
+Name: [anon:atexit handlers]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f7ef9000-7007f7f0d000 r--s 00000000 fe:00 111 /system/fonts/NotoSansKannada-Bold.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7f0d000-7007f7f19000 r--p 00000000 fe:00 1208 /system/lib64/libbufferhubqueue.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 6 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007f7f19000-7007f7f2a000 r-xp 0000c000 fe:00 1208 /system/lib64/libbufferhubqueue.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7f2a000-7007f7f2b000 rw-p 0001d000 fe:00 1208 /system/lib64/libbufferhubqueue.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7f2b000-7007f7f2c000 r--p 0001e000 fe:00 1208 /system/lib64/libbufferhubqueue.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7f2c000-7007f7f2d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7f2d000-7007f7f2e000 rw-s 00000000 00:05 9940 /dev/ashmem/0f02fd80-4c97-473b-981d-e08369625636 (deleted)
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr sh mr mw ms
+7007f7f2e000-7007f7f30000 r--s 00000000 fe:00 112 /system/fonts/NotoSansMro-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7f30000-7007f7f71000 r--s 00000000 fe:00 62 /system/fonts/NotoSerif-BoldItalic.ttf
+Size: 260 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7f71000-7007f7f72000 r--p 00000000 fe:00 1177 /system/lib64/libsync.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f7f72000-7007f7f73000 r-xp 00001000 fe:00 1177 /system/lib64/libsync.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f7f73000-7007f7f74000 rw-p 00002000 fe:00 1177 /system/lib64/libsync.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7f74000-7007f7f75000 r--p 00003000 fe:00 1177 /system/lib64/libsync.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f7f75000-7007f7f76000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f7f76000-7007f7f7a000 r--s 00000000 fe:00 99 /system/fonts/NotoSansSylotiNagri-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7f7a000-7007f7f87000 r--s 00000000 fe:00 125 /system/fonts/NotoSansAdlam-Regular.ttf
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f7f87000-7007f7fd1000 r--p 00000000 fe:00 1597 /system/lib64/libc.so
+Size: 296 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 228 kB
+Pss: 3 kB
+Shared_Clean: 228 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 228 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007f7fd1000-7007f8089000 r-xp 0004a000 fe:00 1597 /system/lib64/libc.so
+Size: 736 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 728 kB
+Pss: 12 kB
+Shared_Clean: 728 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 728 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me
+7007f8089000-7007f808c000 rw-p 00102000 fe:00 1597 /system/lib64/libc.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f808c000-7007f8093000 r--p 00105000 fe:00 1597 /system/lib64/libc.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 28 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f8093000-7007f80e5000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 40 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 40 kB
+Referenced: 48 kB
+Anonymous: 48 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 40 kB
+VmFlags: rd wr mr mw me ac
+7007f80e5000-7007f80e6000 r--p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f80e6000-7007f80eb000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 20 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 20 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd wr mr mw me ac
+7007f80eb000-7007f80ec000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f80ec000-7007f80ee000 r--s 00000000 fe:00 209 /system/fonts/NotoSansHatran-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f80ee000-7007f810a000 r--s 00000000 fe:00 181 /system/fonts/NotoSansTeluguUI-Regular.ttf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f810a000-7007f8113000 r--p 00000000 fe:00 1515 /system/lib64/libsoundtrigger.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 9 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f8113000-7007f8118000 r-xp 00009000 fe:00 1515 /system/lib64/libsoundtrigger.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 10 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd ex mr mw me
+7007f8118000-7007f8119000 rw-p 0000e000 fe:00 1515 /system/lib64/libsoundtrigger.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8119000-7007f811d000 r--p 0000f000 fe:00 1515 /system/lib64/libsoundtrigger.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f811d000-7007f811e000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f811e000-7007f8122000 r--s 00000000 fe:00 107 /system/fonts/NotoSansOldPersian-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8122000-7007f812a000 r--s 00000000 fe:00 116 /system/fonts/NotoSansLaoUI-Bold.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f812a000-7007f814f000 r--s 00000000 fe:00 252 /system/fonts/NotoSerifTelugu-Bold.ttf
+Size: 148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f814f000-7007f815d000 r--p 00000000 fe:00 1190 /system/lib64/libexpat.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 56 kB
+Pss: 9 kB
+Shared_Clean: 56 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 56 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f815d000-7007f8178000 r-xp 0000e000 fe:00 1190 /system/lib64/libexpat.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f8178000-7007f8179000 rw-p 00029000 fe:00 1190 /system/lib64/libexpat.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8179000-7007f817b000 r--p 0002a000 fe:00 1190 /system/lib64/libexpat.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f817b000-7007f817c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f817c000-7007f8182000 r--s 00000000 fe:00 188 /system/fonts/NotoSansKharoshthi-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8182000-7007f81c5000 r--p 00000000 fe:00 1204 /system/lib64/libmedia.so
+Size: 268 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 21 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+7007f81c5000-7007f821a000 r-xp 00043000 fe:00 1204 /system/lib64/libmedia.so
+Size: 340 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 272 kB
+Pss: 65 kB
+Shared_Clean: 272 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 272 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 65 kB
+VmFlags: rd ex mr mw me
+7007f821a000-7007f821b000 rw-p 00098000 fe:00 1204 /system/lib64/libmedia.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f821b000-7007f8230000 r--p 00099000 fe:00 1204 /system/lib64/libmedia.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 84 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 84 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 84 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f8230000-7007f8231000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8231000-7007f8232000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f8232000-7007f8257000 r--s 00000000 fe:00 203 /system/fonts/NotoSerifTelugu-Regular.ttf
+Size: 148 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8257000-7007f8264000 r--p 00000000 fe:00 1700 /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 5 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f8264000-7007f8271000 r-xp 0000d000 fe:00 1700 /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 6 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007f8271000-7007f8272000 rw-p 0001a000 fe:00 1700 /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8272000-7007f8274000 r--p 0001b000 fe:00 1700 /system/lib64/android.hardware.graphics.mapper@2.1.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f8274000-7007f827a000 r--s 00000000 fe:00 106 /system/fonts/NotoSansCoptic-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f827a000-7007f8282000 r--s 00000000 fe:00 59 /system/fonts/NotoSansLaoUI-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8282000-7007f8296000 r--p 00000000 fe:00 1216 /system/lib64/android.system.suspend@1.0.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 9 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f8296000-7007f82ae000 r-xp 00014000 fe:00 1216 /system/lib64/android.system.suspend@1.0.so
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 96 kB
+Pss: 19 kB
+Shared_Clean: 96 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 96 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 19 kB
+VmFlags: rd ex mr mw me
+7007f82ae000-7007f82af000 rw-p 0002c000 fe:00 1216 /system/lib64/android.system.suspend@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f82af000-7007f82b3000 r--p 0002d000 fe:00 1216 /system/lib64/android.system.suspend@1.0.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f82b3000-7007f82b4000 r--s 00000000 00:13 6990 /dev/event-log-tags
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f82b4000-7007f82ba000 r--s 00000000 fe:00 242 /system/fonts/NotoSansBrahmi-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f82ba000-7007f82c4000 r--s 00000000 fe:00 92 /system/fonts/NotoSansKhmerUI-Regular.ttf
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f82c4000-7007f82c5000 r--p 00000000 fe:00 1217 /system/lib64/libhidlallocatorutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f82c5000-7007f82c6000 r-xp 00001000 fe:00 1217 /system/lib64/libhidlallocatorutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f82c6000-7007f82c7000 rw-p 00002000 fe:00 1217 /system/lib64/libhidlallocatorutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f82c7000-7007f82c8000 r--p 00003000 fe:00 1217 /system/lib64/libhidlallocatorutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f82c8000-7007f82ce000 r--s 00000000 fe:00 101 /system/fonts/NotoSansCham-Bold.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f82ce000-7007f82da000 r--s 00000000 fe:00 228 /system/fonts/NotoSerifKhmer-Bold.otf
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f82da000-7007f832b000 r--s 00000000 fe:00 89 /system/fonts/Roboto-ThinItalic.ttf
+Size: 324 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f832b000-7007f835c000 r--p 00000000 fe:00 1152 /system/lib64/libvintf.so
+Size: 196 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 32 kB
+Shared_Clean: 124 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 124 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd mr mw me
+7007f835c000-7007f83b7000 r-xp 00031000 fe:00 1152 /system/lib64/libvintf.so
+Size: 364 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 360 kB
+Pss: 138 kB
+Shared_Clean: 352 kB
+Shared_Dirty: 0 kB
+Private_Clean: 8 kB
+Private_Dirty: 0 kB
+Referenced: 360 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 138 kB
+VmFlags: rd ex mr mw me
+7007f83b7000-7007f83b8000 rw-p 0008c000 fe:00 1152 /system/lib64/libvintf.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f83b8000-7007f83bd000 r--p 0008d000 fe:00 1152 /system/lib64/libvintf.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f83bd000-7007f83bf000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f83bf000-7007f83c2000 r--s 00000000 fe:00 287 /system/fonts/NotoSansTaiLe-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f83c2000-7007f85b0000 r--p 00000000 fe:00 1537 /system/lib64/libhwui.so
+Size: 1976 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 632 kB
+Pss: 196 kB
+Shared_Clean: 632 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 632 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 196 kB
+VmFlags: rd mr mw me
+7007f85b0000-7007f8b9b000 r-xp 001ee000 fe:00 1537 /system/lib64/libhwui.so
+Size: 6060 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4132 kB
+Pss: 1274 kB
+Shared_Clean: 4132 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4132 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1274 kB
+VmFlags: rd ex mr mw me
+7007f8b9b000-7007f8b9c000 rw-p 007d9000 fe:00 1537 /system/lib64/libhwui.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f8b9c000-7007f8bd5000 r--p 007da000 fe:00 1537 /system/lib64/libhwui.so
+Size: 228 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 228 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 228 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 160 kB
+Anonymous: 228 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me ac
+7007f8bd5000-7007f8bda000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 16 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 16 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd wr mr mw me ac
+7007f8bda000-7007f8bde000 r--s 00000000 fe:00 126 /system/fonts/NotoSansNewTaiLue-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8bde000-7007f8bf3000 r--s 00000000 fe:00 90 /system/fonts/NotoSansKannada-Regular.ttf
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8bf3000-7007f8c0e000 r--s 00000000 fe:00 118 /system/fonts/NotoSansTelugu-Bold.ttf
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8c0e000-7007f8cfe000 r--p 00000000 fe:00 1538 /system/lib64/libicui18n.so
+Size: 960 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 192 kB
+Pss: 21 kB
+Shared_Clean: 192 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 192 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd mr mw me
+7007f8cfe000-7007f8e8a000 r-xp 000f0000 fe:00 1538 /system/lib64/libicui18n.so
+Size: 1584 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 1276 kB
+Pss: 318 kB
+Shared_Clean: 1276 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 1276 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 318 kB
+VmFlags: rd ex mr mw me
+7007f8e8a000-7007f8e8b000 rw-p 0027c000 fe:00 1538 /system/lib64/libicui18n.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8e8b000-7007f8e9e000 r--p 0027d000 fe:00 1538 /system/lib64/libicui18n.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 76 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 76 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 76 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f8e9e000-7007f8e9f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f8e9f000-7007f8ea7000 r--s 00000000 fe:00 95 /system/fonts/NotoSerifLao-Bold.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8ea7000-7007f8ec3000 r--s 00000000 fe:00 221 /system/fonts/NotoSansTelugu-Regular.ttf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8ec3000-7007f8edd000 r--s 00000000 fe:00 64 /system/fonts/NotoSansBengaliUI-Bold.ttf
+Size: 104 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8edd000-7007f8edf000 r--p 00000000 fe:00 1743 /system/lib64/libstagefright_codecbase.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 1 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f8edf000-7007f8ee2000 r-xp 00002000 fe:00 1743 /system/lib64/libstagefright_codecbase.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 6 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007f8ee2000-7007f8ee3000 rw-p 00005000 fe:00 1743 /system/lib64/libstagefright_codecbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8ee3000-7007f8ee4000 r--p 00006000 fe:00 1743 /system/lib64/libstagefright_codecbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f8ee4000-7007f8ee7000 r--s 00000000 fe:00 176 /system/fonts/NotoSansSundanese-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8ee7000-7007f8f04000 r--s 00000000 fe:00 197 /system/fonts/NotoSerifBengali-Bold.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8f04000-7007f8f05000 r--p 00000000 fe:00 1151 /system/lib64/libion.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f8f05000-7007f8f06000 r-xp 00001000 fe:00 1151 /system/lib64/libion.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f8f06000-7007f8f07000 rw-p 00002000 fe:00 1151 /system/lib64/libion.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8f07000-7007f8f08000 r--p 00003000 fe:00 1151 /system/lib64/libion.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f8f08000-7007f8f09000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f8f09000-7007f8f0a000 r--s 00000000 fe:00 996 /system/usr/hyphen-data/hyph-und-ethi.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8f0a000-7007f8f55000 r--s 00000000 fe:00 237 /system/fonts/Roboto-Light.ttf
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f8f55000-7007f9019000 r--p 00000000 fe:00 1195 /system/lib64/libandroid_runtime.so
+Size: 784 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 448 kB
+Pss: 78 kB
+Shared_Clean: 448 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 448 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 78 kB
+VmFlags: rd mr mw me
+7007f9019000-7007f910c000 r-xp 000c4000 fe:00 1195 /system/lib64/libandroid_runtime.so
+Size: 972 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 772 kB
+Pss: 68 kB
+Shared_Clean: 772 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 772 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 68 kB
+VmFlags: rd ex mr mw me
+7007f910c000-7007f910d000 rw-p 001b7000 fe:00 1195 /system/lib64/libandroid_runtime.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f910d000-7007f912a000 r--p 001b8000 fe:00 1195 /system/lib64/libandroid_runtime.so
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 116 kB
+Pss: 6 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 116 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 72 kB
+Anonymous: 116 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me ac
+7007f912a000-7007f912d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+7007f912d000-7007f9148000 r--s 00000000 fe:00 67 /system/fonts/NotoSansBengaliUI-Regular.ttf
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9148000-7007f9158000 r--p 00000000 fe:00 1761 /system/lib64/android.hardware.configstore@1.0.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 6 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007f9158000-7007f916c000 r-xp 00010000 fe:00 1761 /system/lib64/android.hardware.configstore@1.0.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 10 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd ex mr mw me
+7007f916c000-7007f916d000 rw-p 00024000 fe:00 1761 /system/lib64/android.hardware.configstore@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f916d000-7007f9170000 r--p 00025000 fe:00 1761 /system/lib64/android.hardware.configstore@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9170000-7007f9172000 r--s 00000000 fe:00 79 /system/fonts/NotoSansBassaVah-Regular.otf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9172000-7007f918f000 r--s 00000000 fe:00 246 /system/fonts/NotoSerifBengali-Regular.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f918f000-7007f91b2000 r--p 00000000 fe:00 1581 /system/lib64/libsqlite.so
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 7 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f91b2000-7007f92b8000 r-xp 00023000 fe:00 1581 /system/lib64/libsqlite.so
+Size: 1048 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 916 kB
+Pss: 111 kB
+Shared_Clean: 916 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 916 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 111 kB
+VmFlags: rd ex mr mw me
+7007f92b8000-7007f92ba000 rw-p 00129000 fe:00 1581 /system/lib64/libsqlite.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f92ba000-7007f92bd000 r--p 0012b000 fe:00 1581 /system/lib64/libsqlite.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f92bd000-7007f92be000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f92be000-7007f92c1000 r--s 00000000 fe:00 65 /system/fonts/NotoSansSamaritan-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f92c1000-7007f92cb000 r--s 00000000 fe:00 174 /system/fonts/NotoSerifKhmer-Regular.otf
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f92cb000-7007f92ed000 r--p 00000000 fe:00 1127 /system/lib64/libm.so
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 136 kB
+Pss: 7 kB
+Shared_Clean: 136 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 136 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f92ed000-7007f9319000 r-xp 00022000 fe:00 1127 /system/lib64/libm.so
+Size: 176 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 100 kB
+Pss: 4 kB
+Shared_Clean: 100 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 100 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+7007f9319000-7007f931a000 rw-p 0004e000 fe:00 1127 /system/lib64/libm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f931a000-7007f931b000 r--p 0004f000 fe:00 1127 /system/lib64/libm.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f931b000-7007f931c000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f931c000-7007f9366000 r--s 00000000 fe:00 175 /system/fonts/RobotoCondensed-Light.ttf
+Size: 296 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9366000-7007f936e000 r--p 00000000 fe:00 1260 /system/lib64/libcutils.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 0 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f936e000-7007f9377000 r-xp 00008000 fe:00 1260 /system/lib64/libcutils.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 0 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9377000-7007f9378000 rw-p 00011000 fe:00 1260 /system/lib64/libcutils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f9378000-7007f937a000 r--p 00012000 fe:00 1260 /system/lib64/libcutils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f937a000-7007f937b000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f937b000-7007f937c000 r--s 00000000 fe:00 1018 /system/usr/hyphen-data/hyph-tk.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f937c000-7007f9384000 r--s 00000000 fe:00 117 /system/fonts/NotoSerifLao-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9384000-7007f93b3000 r--p 00000000 fe:00 1598 /system/lib64/libft2.so
+Size: 188 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 26 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 26 kB
+VmFlags: rd mr mw me
+7007f93b3000-7007f9422000 r-xp 0002f000 fe:00 1598 /system/lib64/libft2.so
+Size: 444 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 376 kB
+Pss: 94 kB
+Shared_Clean: 376 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 376 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 94 kB
+VmFlags: rd ex mr mw me
+7007f9422000-7007f9423000 rw-p 0009e000 fe:00 1598 /system/lib64/libft2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9423000-7007f9428000 r--p 0009f000 fe:00 1598 /system/lib64/libft2.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f9428000-7007f942c000 r--s 00000000 fe:00 217 /system/fonts/NotoSansMeeteiMayek-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f942c000-7007f9445000 r--s 00000000 fe:00 127 /system/fonts/NotoSansBengali-Bold.ttf
+Size: 100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9445000-7007f9452000 r--p 00000000 fe:00 1520 /system/lib64/libhidl-gen-utils.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 5 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f9452000-7007f9467000 r-xp 0000d000 fe:00 1520 /system/lib64/libhidl-gen-utils.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 84 kB
+Pss: 11 kB
+Shared_Clean: 84 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 84 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd ex mr mw me
+7007f9467000-7007f9468000 rw-p 00022000 fe:00 1520 /system/lib64/libhidl-gen-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9468000-7007f9469000 r--p 00023000 fe:00 1520 /system/lib64/libhidl-gen-utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9469000-7007f946a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f946a000-7007f946c000 r--s 00000000 fe:00 272 /system/fonts/NotoSansUgaritic-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f946c000-7007f9485000 r--s 00000000 fe:00 183 /system/fonts/NotoSansBengali-Regular.ttf
+Size: 100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9485000-7007f9493000 r--s 00000000 fe:00 94 /system/fonts/NotoSansMalayalamUI-Bold.ttf
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9493000-7007f9495000 r--p 00000000 fe:00 1189 /system/lib64/libnativebridge.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9495000-7007f9497000 r-xp 00002000 fe:00 1189 /system/lib64/libnativebridge.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9497000-7007f9498000 rw-p 00004000 fe:00 1189 /system/lib64/libnativebridge.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9498000-7007f9499000 r--p 00005000 fe:00 1189 /system/lib64/libnativebridge.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9499000-7007f949a000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f949a000-7007f949e000 r--s 00000000 fe:00 145 /system/fonts/NotoSansMandaic-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f949e000-7007f94ac000 r--s 00000000 fe:00 133 /system/fonts/NotoSansMalayalamUI-Regular.ttf
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f94ac000-7007f94b9000 r--s 00000000 fe:00 232 /system/fonts/NotoSerifMalayalam-Bold.ttf
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f94b9000-7007f94c7000 r--s 00000000 fe:00 273 /system/fonts/NotoSansMalayalam-Bold.ttf
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f94c7000-7007f94f3000 r--p 00000000 fe:00 1702 /system/lib64/libharfbuzz_ng.so
+Size: 176 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 156 kB
+Pss: 33 kB
+Shared_Clean: 156 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 156 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 33 kB
+VmFlags: rd mr mw me
+7007f94f3000-7007f9570000 r-xp 0002c000 fe:00 1702 /system/lib64/libharfbuzz_ng.so
+Size: 500 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 488 kB
+Pss: 122 kB
+Shared_Clean: 488 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 488 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 122 kB
+VmFlags: rd ex mr mw me
+7007f9570000-7007f9571000 rw-p 000a9000 fe:00 1702 /system/lib64/libharfbuzz_ng.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9571000-7007f9572000 r--p 000aa000 fe:00 1702 /system/lib64/libharfbuzz_ng.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9572000-7007f9575000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9575000-7007f957e000 r--s 00000000 fe:00 212 /system/fonts/NotoSansKhmerUI-Bold.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f957e000-7007f958c000 r--s 00000000 fe:00 150 /system/fonts/NotoSansMalayalam-Regular.ttf
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f958c000-7007f958e000 r--p 00000000 fe:00 1640 /system/lib64/libmemtrack.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f958e000-7007f958f000 r-xp 00002000 fe:00 1640 /system/lib64/libmemtrack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd ex mr mw me
+7007f958f000-7007f9590000 rw-p 00003000 fe:00 1640 /system/lib64/libmemtrack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9590000-7007f9591000 r--p 00004000 fe:00 1640 /system/lib64/libmemtrack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9591000-7007f9592000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f9592000-7007f959f000 r--s 00000000 fe:00 109 /system/fonts/NotoSerifMalayalam-Regular.ttf
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f959f000-7007f95a8000 r--s 00000000 fe:00 163 /system/fonts/NotoSansTamilUI-Bold.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f95a8000-7007f95f4000 r--s 00000000 fe:00 251 /system/fonts/Roboto-Thin.ttf
+Size: 304 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f95f4000-7007f95f6000 r--p 00000000 fe:00 1657 /system/lib64/libdebuggerd_client.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 1 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f95f6000-7007f95f8000 r-xp 00002000 fe:00 1657 /system/lib64/libdebuggerd_client.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f95f8000-7007f95f9000 rw-p 00004000 fe:00 1657 /system/lib64/libdebuggerd_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f95f9000-7007f95fa000 r--p 00005000 fe:00 1657 /system/lib64/libdebuggerd_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f95fa000-7007f95fe000 r--s 00000000 fe:00 201 /system/fonts/NotoSansGlagolitic-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f95fe000-7007f961a000 r--s 00000000 fe:00 69 /system/fonts/NotoSansGujaratiUI-Bold.ttf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f961a000-7007f961b000 r--p 00000000 fe:00 1652 /system/lib64/libdl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f961b000-7007f961c000 r-xp 00001000 fe:00 1652 /system/lib64/libdl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f961c000-7007f961d000 r--p 00002000 fe:00 1652 /system/lib64/libdl.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f961d000-7007f961e000 r--p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f961e000-7007f9624000 r--s 00000000 fe:00 173 /system/fonts/NotoSansCham-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9624000-7007f9642000 r--s 00000000 fe:00 271 /system/fonts/NotoSansGujarati-Regular.ttf
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9642000-7007f9648000 r--p 00000000 fe:00 1769 /system/lib64/libbpf_android.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 4 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007f9648000-7007f9652000 r-xp 00006000 fe:00 1769 /system/lib64/libbpf_android.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+7007f9652000-7007f9653000 rw-p 00010000 fe:00 1769 /system/lib64/libbpf_android.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9653000-7007f9654000 r--p 00011000 fe:00 1769 /system/lib64/libbpf_android.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9654000-7007f9655000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9655000-7007f9657000 r--s 00000000 fe:00 177 /system/fonts/NotoSansTagbanwa-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9657000-7007f9660000 r--s 00000000 fe:00 75 /system/fonts/NotoSansTamilUI-Regular.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9660000-7007f969d000 r--s 00000000 fe:00 140 /system/fonts/NotoSerif-Italic.ttf
+Size: 244 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f969d000-7007f969f000 r--p 00000000 fe:00 1718 /system/lib64/libstdc++.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f969f000-7007f96a1000 r-xp 00002000 fe:00 1718 /system/lib64/libstdc++.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f96a1000-7007f96a2000 rw-p 00004000 fe:00 1718 /system/lib64/libstdc++.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f96a2000-7007f96a3000 r--p 00005000 fe:00 1718 /system/lib64/libstdc++.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f96a3000-7007f96c0000 r--s 00000000 fe:00 256 /system/fonts/NotoSansGujaratiUI-Regular.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f96c0000-7007f96de000 r--s 00000000 fe:00 93 /system/fonts/NotoSansDevanagariUI-Bold.ttf
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f96de000-7007f9717000 r--p 00000000 fe:00 1547 /system/lib64/libhidltransport.so
+Size: 228 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 200 kB
+Pss: 9 kB
+Shared_Clean: 200 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 200 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007f9717000-7007f9772000 r-xp 00039000 fe:00 1547 /system/lib64/libhidltransport.so
+Size: 364 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 364 kB
+Pss: 16 kB
+Shared_Clean: 364 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 364 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 16 kB
+VmFlags: rd ex mr mw me
+7007f9772000-7007f9773000 rw-p 00094000 fe:00 1547 /system/lib64/libhidltransport.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9773000-7007f977d000 r--p 00095000 fe:00 1547 /system/lib64/libhidltransport.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 40 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 40 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me ac
+7007f977d000-7007f977e000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f977e000-7007f9786000 r--s 00000000 fe:00 185 /system/fonts/NotoSansLao-Bold.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9786000-7007f978f000 r--s 00000000 fe:00 245 /system/fonts/NotoSerifTamil-Bold.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f978f000-7007f9798000 r--s 00000000 fe:00 191 /system/fonts/NotoSerifTamil-Regular.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9798000-7007f97b7000 r--s 00000000 fe:00 248 /system/fonts/NotoSansDevanagariUI-Regular.ttf
+Size: 124 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f97b7000-7007f986a000 r--p 00000000 fe:00 1267 /system/lib64/libicuuc.so
+Size: 716 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 440 kB
+Pss: 45 kB
+Shared_Clean: 440 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 440 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 45 kB
+VmFlags: rd mr mw me
+7007f986a000-7007f9966000 r-xp 000b3000 fe:00 1267 /system/lib64/libicuuc.so
+Size: 1008 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 992 kB
+Pss: 73 kB
+Shared_Clean: 992 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 992 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 73 kB
+VmFlags: rd ex mr mw me
+7007f9966000-7007f9967000 rw-p 001af000 fe:00 1267 /system/lib64/libicuuc.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9967000-7007f997b000 r--p 001b0000 fe:00 1267 /system/lib64/libicuuc.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 80 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 80 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007f997b000-7007f997d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007f997d000-7007f997e000 r--s 00000000 fe:00 977 /system/usr/hyphen-data/hyph-te.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f997e000-7007f9987000 r--s 00000000 fe:00 70 /system/fonts/NotoSansTamil-Bold.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9987000-7007f99a8000 r--s 00000000 fe:00 124 /system/fonts/NotoSansDevanagari-Bold.ttf
+Size: 132 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f99a8000-7007f99ac000 r--p 00000000 fe:00 1542 /system/lib64/libstagefright_xmlparser.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 3 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007f99ac000-7007f99b4000 r-xp 00004000 fe:00 1542 /system/lib64/libstagefright_xmlparser.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f99b4000-7007f99b5000 rw-p 0000c000 fe:00 1542 /system/lib64/libstagefright_xmlparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f99b5000-7007f99b6000 r--p 0000d000 fe:00 1542 /system/lib64/libstagefright_xmlparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f99b6000-7007f99b8000 r--s 00000000 fe:00 234 /system/fonts/NotoSansTagalog-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f99b8000-7007f99da000 r--s 00000000 fe:00 235 /system/fonts/NotoSansDevanagari-Regular.ttf
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f99da000-7007f99e1000 r--p 00000000 fe:00 1555 /system/lib64/libstatslog.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 5 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd mr mw me
+7007f99e1000-7007f99ef000 r-xp 00007000 fe:00 1555 /system/lib64/libstatslog.so
+Size: 56 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 56 kB
+Pss: 10 kB
+Shared_Clean: 56 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 56 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd ex mr mw me
+7007f99ef000-7007f99f0000 rw-p 00015000 fe:00 1555 /system/lib64/libstatslog.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f99f0000-7007f99f1000 r--p 00016000 fe:00 1555 /system/lib64/libstatslog.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f99f1000-7007f99f2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f99f2000-7007f99fa000 r--s 00000000 fe:00 73 /system/fonts/NotoSansLao-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f99fa000-7007f9a03000 r--s 00000000 fe:00 205 /system/fonts/NotoSansTamil-Regular.ttf
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9a03000-7007f9a13000 r--p 00000000 fe:00 1224 /system/lib64/android.hidl.memory@1.0.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007f9a13000-7007f9a27000 r-xp 00010000 fe:00 1224 /system/lib64/android.hidl.memory@1.0.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 20 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 20 kB
+VmFlags: rd ex mr mw me
+7007f9a27000-7007f9a28000 rw-p 00024000 fe:00 1224 /system/lib64/android.hidl.memory@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9a28000-7007f9a2b000 r--p 00025000 fe:00 1224 /system/lib64/android.hidl.memory@1.0.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9a2b000-7007f9a48000 r--s 00000000 fe:00 171 /system/fonts/NotoSansGujarati-Bold.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9a48000-7007f9a51000 r--p 00000000 fe:00 1723 /system/lib64/libbase.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 0 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9a51000-7007f9a5a000 r-xp 00009000 fe:00 1723 /system/lib64/libbase.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 0 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9a5a000-7007f9a5b000 rw-p 00012000 fe:00 1723 /system/lib64/libbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9a5b000-7007f9a5c000 r--p 00013000 fe:00 1723 /system/lib64/libbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9a5c000-7007f9a5d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f9a5d000-7007f9a5f000 r--s 00000000 fe:00 66 /system/fonts/NotoSansShavian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9a5f000-7007f9a73000 r--s 00000000 fe:00 264 /system/fonts/NotoSerifDevanagari-Bold.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9a73000-7007f9a93000 r--s 00000000 fe:00 266 /system/fonts/NotoSerifEthiopic-Bold.otf
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9a93000-7007f9a96000 r--p 00000000 fe:00 1519 /system/lib64/libnativeloader.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9a96000-7007f9a9a000 r-xp 00003000 fe:00 1519 /system/lib64/libnativeloader.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9a9a000-7007f9a9b000 rw-p 00007000 fe:00 1519 /system/lib64/libnativeloader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9a9b000-7007f9a9c000 r--p 00008000 fe:00 1519 /system/lib64/libnativeloader.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9a9c000-7007f9a9d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f9a9d000-7007f9aa0000 r--s 00000000 fe:00 136 /system/fonts/NotoSansRunic-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9aa0000-7007f9aa8000 r--s 00000000 fe:00 143 /system/fonts/NotoSansGurmukhiUI-Bold.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9aa8000-7007f9abc000 r--s 00000000 fe:00 260 /system/fonts/NotoSerifDevanagari-Regular.ttf
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9abc000-7007f9ad8000 r--s 00000000 fe:00 215 /system/fonts/NotoSerifEthiopic-Regular.otf
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9ad8000-7007f9b30000 r--p 00000000 fe:00 1656 /system/lib64/libc++.so
+Size: 352 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 224 kB
+Pss: 3 kB
+Shared_Clean: 224 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 224 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007f9b30000-7007f9ba4000 r-xp 00058000 fe:00 1656 /system/lib64/libc++.so
+Size: 464 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 388 kB
+Pss: 6 kB
+Shared_Clean: 388 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 388 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007f9ba4000-7007f9ba5000 rw-p 000cc000 fe:00 1656 /system/lib64/libc++.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9ba5000-7007f9bad000 r--p 000cd000 fe:00 1656 /system/lib64/libc++.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 32 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f9bad000-7007f9bb1000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 16 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+7007f9bb1000-7007f9bb3000 r--s 00000000 fe:00 146 /system/fonts/NotoSansRejang-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9bb3000-7007f9bf0000 r--s 00000000 fe:00 113 /system/fonts/NotoSerif-Bold.ttf
+Size: 244 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9bf0000-7007f9bf1000 r--p 00000000 fe:00 1614 /system/lib64/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9bf1000-7007f9bf2000 r-xp 00001000 fe:00 1614 /system/lib64/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9bf2000-7007f9bf3000 rw-p 00002000 fe:00 1614 /system/lib64/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9bf3000-7007f9bf4000 r--p 00003000 fe:00 1614 /system/lib64/libhardware.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9bf4000-7007f9bfc000 r--s 00000000 fe:00 97 /system/fonts/NotoSansGurmukhiUI-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9bfc000-7007f9c04000 r--s 00000000 fe:00 250 /system/fonts/NotoSerifGurmukhi-Bold.otf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9c04000-7007f9c26000 r--p 00000000 fe:00 1714 /system/lib64/libartbase.so
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 108 kB
+Pss: 7 kB
+Shared_Clean: 108 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 108 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f9c26000-7007f9c72000 r-xp 00022000 fe:00 1714 /system/lib64/libartbase.so
+Size: 304 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 164 kB
+Pss: 7 kB
+Shared_Clean: 164 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 164 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd ex mr mw me
+7007f9c72000-7007f9c73000 rw-p 0006e000 fe:00 1714 /system/lib64/libartbase.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9c73000-7007f9c75000 r--p 0006f000 fe:00 1714 /system/lib64/libartbase.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9c75000-7007f9c7c000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007f9c7c000-7007f9c84000 r--s 00000000 fe:00 78 /system/fonts/NotoSansGurmukhi-Bold.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9c84000-7007f9ca8000 r--s 00000000 fe:00 218 /system/fonts/NotoSansEthiopic-Bold.ttf
+Size: 144 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9ca8000-7007f9caa000 r--p 00000000 fe:00 1258 /system/lib64/libdexfile_external.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9caa000-7007f9cad000 r-xp 00002000 fe:00 1258 /system/lib64/libdexfile_external.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9cad000-7007f9cae000 rw-p 00005000 fe:00 1258 /system/lib64/libdexfile_external.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9cae000-7007f9caf000 r--p 00006000 fe:00 1258 /system/lib64/libdexfile_external.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9caf000-7007f9cb0000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9cb0000-7007f9cb7000 r--s 00000000 fe:00 179 /system/fonts/NotoSerifGurmukhi-Regular.otf
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9cb7000-7007f9cda000 r--s 00000000 fe:00 77 /system/fonts/NotoSansEthiopic-Regular.ttf
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9cda000-7007f9ce3000 r--p 00000000 fe:00 1571 /system/lib64/libtinyxml2.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 7 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f9ce3000-7007f9cef000 r-xp 00009000 fe:00 1571 /system/lib64/libtinyxml2.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 24 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd ex mr mw me
+7007f9cef000-7007f9cf0000 rw-p 00015000 fe:00 1571 /system/lib64/libtinyxml2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9cf0000-7007f9cf1000 r--p 00016000 fe:00 1571 /system/lib64/libtinyxml2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9cf1000-7007f9cf2000 r--s 00000000 fe:00 974 /system/usr/hyphen-data/hyph-ta.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9cf2000-7007f9cf6000 r--s 00000000 fe:00 247 /system/fonts/NotoSansBatak-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9cf6000-7007f9d14000 r--s 00000000 fe:00 253 /system/fonts/NotoNaskhArabicUI-Bold.ttf
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9d14000-7007f9d16000 r--p 00000000 fe:00 1256 /system/lib64/libusbhost.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 2 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007f9d16000-7007f9d18000 r-xp 00002000 fe:00 1256 /system/lib64/libusbhost.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9d18000-7007f9d19000 rw-p 00004000 fe:00 1256 /system/lib64/libusbhost.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9d19000-7007f9d1a000 r--p 00005000 fe:00 1256 /system/lib64/libusbhost.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9d1a000-7007f9d1e000 r--s 00000000 fe:00 240 /system/fonts/NotoSansAhom-Regular.otf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9d1e000-7007f9d26000 r--s 00000000 fe:00 224 /system/fonts/NotoSansGurmukhi-Regular.ttf
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9d26000-7007f9d63000 r--s 00000000 fe:00 91 /system/fonts/NotoSerif-Regular.ttf
+Size: 244 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9d63000-7007f9dae000 r--p 00000000 fe:00 1681 /system/lib64/libdng_sdk.so
+Size: 300 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 116 kB
+Pss: 33 kB
+Shared_Clean: 116 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 116 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 33 kB
+VmFlags: rd mr mw me
+7007f9dae000-7007f9e37000 r-xp 0004b000 fe:00 1681 /system/lib64/libdng_sdk.so
+Size: 548 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9e37000-7007f9e38000 rw-p 000d4000 fe:00 1681 /system/lib64/libdng_sdk.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9e38000-7007f9e3e000 r--p 000d5000 fe:00 1681 /system/lib64/libdng_sdk.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 24 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007f9e3e000-7007f9e3f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9e3f000-7007f9e40000 r--s 00000000 fe:00 1016 /system/usr/hyphen-data/hyph-pt.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9e40000-7007f9e50000 r--s 00000000 fe:00 152 /system/fonts/NotoSerifGujarati-Bold.ttf
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9e50000-7007f9e82000 r--p 00000000 fe:00 1221 /system/lib64/libcamera_client.so
+Size: 200 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007f9e82000-7007f9ea4000 r-xp 00032000 fe:00 1221 /system/lib64/libcamera_client.so
+Size: 136 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9ea4000-7007f9ea5000 rw-p 00054000 fe:00 1221 /system/lib64/libcamera_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9ea5000-7007f9eb1000 r--p 00055000 fe:00 1221 /system/lib64/libcamera_client.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 48 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 48 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me ac
+7007f9eb1000-7007f9eb2000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9eb2000-7007f9eb3000 r--s 00000000 fe:00 1002 /system/usr/hyphen-data/hyph-pa.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9eb3000-7007f9ec3000 r--s 00000000 fe:00 238 /system/fonts/NotoSerifGujarati-Regular.ttf
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9ec3000-7007f9ed6000 r--p 00000000 fe:00 1528 /system/lib64/libGLESv2.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 7 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd mr mw me
+7007f9ed6000-7007f9edd000 r-xp 00013000 fe:00 1528 /system/lib64/libGLESv2.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 7 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd ex mr mw me
+7007f9edd000-7007f9ede000 rw-p 0001a000 fe:00 1528 /system/lib64/libGLESv2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9ede000-7007f9edf000 r--p 0001b000 fe:00 1528 /system/lib64/libGLESv2.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9edf000-7007f9ee0000 r--s 00000000 fe:00 975 /system/usr/hyphen-data/hyph-or.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9ee0000-7007f9efe000 r--s 00000000 fe:00 63 /system/fonts/NotoNaskhArabicUI-Regular.ttf
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9efe000-7007f9f1b000 r--s 00000000 fe:00 202 /system/fonts/NotoNaskhArabic-Bold.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f1b000-7007f9f20000 r--p 00000000 fe:00 1104 /system/lib64/libziparchive.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 0 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007f9f20000-7007f9f26000 r-xp 00005000 fe:00 1104 /system/lib64/libziparchive.so
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 1 kB
+Shared_Clean: 24 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+7007f9f26000-7007f9f27000 rw-p 0000b000 fe:00 1104 /system/lib64/libziparchive.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9f27000-7007f9f28000 r--p 0000c000 fe:00 1104 /system/lib64/libziparchive.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9f28000-7007f9f2c000 r--s 00000000 fe:00 280 /system/fonts/NotoSansThaana-Bold.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f2c000-7007f9f32000 r--s 00000000 fe:00 74 /system/fonts/NotoSerifGeorgian-Bold.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f32000-7007f9f4f000 r--s 00000000 fe:00 122 /system/fonts/NotoNaskhArabic-Regular.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f4f000-7007f9f50000 r--p 00000000 fe:00 1709 /system/lib64/libETC1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f9f50000-7007f9f52000 r-xp 00001000 fe:00 1709 /system/lib64/libETC1.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9f52000-7007f9f53000 rw-p 00003000 fe:00 1709 /system/lib64/libETC1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9f53000-7007f9f54000 r--p 00004000 fe:00 1709 /system/lib64/libETC1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9f54000-7007f9f5a000 r--s 00000000 fe:00 262 /system/fonts/NotoSerifGeorgian-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f5a000-7007f9f5f000 r--s 00000000 fe:00 170 /system/fonts/NotoSansGeorgian-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f5f000-7007f9f64000 r--s 00000000 fe:00 290 /system/fonts/NotoSansGeorgian-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f64000-7007f9f69000 r--s 00000000 fe:00 68 /system/fonts/NotoSansThaiUI-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f69000-7007f9f6f000 r--s 00000000 fe:00 134 /system/fonts/NotoSansThaiUI-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f6f000-7007f9f8c000 r--s 00000000 fe:00 222 /system/fonts/DancingScript-Bold.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f8c000-7007f9f8d000 r--p 00000000 fe:00 1663 /system/lib64/libstagefright_http_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007f9f8d000-7007f9f8f000 r-xp 00001000 fe:00 1663 /system/lib64/libstagefright_http_support.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007f9f8f000-7007f9f90000 rw-p 00003000 fe:00 1663 /system/lib64/libstagefright_http_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007f9f90000-7007f9f91000 r--p 00004000 fe:00 1663 /system/lib64/libstagefright_http_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007f9f91000-7007f9f92000 r--s 00000000 fe:00 1031 /system/usr/hyphen-data/hyph-mr.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f92000-7007f9f94000 r--s 00000000 fe:00 123 /system/fonts/NotoSansPhoenician-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9f94000-7007f9fb1000 r--s 00000000 fe:00 103 /system/fonts/DancingScript-Regular.ttf
+Size: 116 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9fb1000-7007f9fcc000 r--s 00000000 fe:00 135 /system/fonts/DroidSansMono.ttf
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007f9fcc000-7007f9fe7000 r--p 00000000 fe:00 1230 /system/lib64/libdexfile.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 4 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 80 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007f9fe7000-7007fa014000 r-xp 0001b000 fe:00 1230 /system/lib64/libdexfile.so
+Size: 180 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 176 kB
+Pss: 9 kB
+Shared_Clean: 176 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 176 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd ex mr mw me
+7007fa014000-7007fa015000 rw-p 00048000 fe:00 1230 /system/lib64/libdexfile.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa015000-7007fa017000 r--p 00049000 fe:00 1230 /system/lib64/libdexfile.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa017000-7007fa01b000 r--s 00000000 fe:00 263 /system/fonts/NotoSansThaana-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa01b000-7007fa03b000 r--s 00000000 00:13 6728 /dev/__properties__/u:object_r:persist_debug_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr me ms
+7007fa03b000-7007fa075000 r--s 00000000 07:08 18 /apex/com.android.tzdata/etc/icu/icu_tzdata.dat
+Size: 232 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 204 kB
+Pss: 24 kB
+Shared_Clean: 204 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 204 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 24 kB
+VmFlags: rd mr me ms
+7007fa075000-7007fa077000 r--p 00000000 fe:00 1575 /system/lib64/libspeexresampler.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa077000-7007fa07a000 r-xp 00002000 fe:00 1575 /system/lib64/libspeexresampler.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa07a000-7007fa07b000 rw-p 00005000 fe:00 1575 /system/lib64/libspeexresampler.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa07b000-7007fa07c000 r--p 00006000 fe:00 1575 /system/lib64/libspeexresampler.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa07c000-7007fa081000 r--s 00000000 fe:00 249 /system/fonts/NotoSerifThai-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa081000-7007fa09f000 r--s 001db000 fe:00 3224 /system/framework/ext.jar
+Size: 120 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa09f000-7007fa0a7000 r--p 00000000 fe:00 1624 /system/lib64/libz.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 1 kB
+Shared_Clean: 32 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 32 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007fa0a7000-7007fa0b7000 r-xp 00008000 fe:00 1624 /system/lib64/libz.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 5 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 5 kB
+VmFlags: rd ex mr mw me
+7007fa0b7000-7007fa0b8000 rw-p 00018000 fe:00 1624 /system/lib64/libz.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa0b8000-7007fa0b9000 r--p 00019000 fe:00 1624 /system/lib64/libz.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa0b9000-7007fa0ba000 r--s 00000000 fe:00 1015 /system/usr/hyphen-data/hyph-ml.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0ba000-7007fa0bf000 r--s 00000000 fe:00 278 /system/fonts/NotoSerifThai-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0bf000-7007fa0c4000 r--s 00000000 fe:00 243 /system/fonts/NotoSansThai-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0c4000-7007fa0ca000 r--s 00000000 fe:00 128 /system/fonts/NotoSansThai-Regular.ttf
+Size: 24 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0ca000-7007fa0cf000 r--s 00000000 fe:00 279 /system/fonts/NotoSerifHebrew-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0cf000-7007fa0ef000 r--s 00000000 00:13 6707 /dev/__properties__/u:object_r:exported2_default_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0ef000-7007fa0f0000 r--p 00000000 fe:00 1574 /system/lib64/libpackagelistparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa0f0000-7007fa0f1000 r-xp 00001000 fe:00 1574 /system/lib64/libpackagelistparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa0f1000-7007fa0f2000 rw-p 00002000 fe:00 1574 /system/lib64/libpackagelistparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa0f2000-7007fa0f3000 r--p 00003000 fe:00 1574 /system/lib64/libpackagelistparser.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa0f3000-7007fa0f6000 r--s 00000000 fe:00 207 /system/fonts/NotoSansOsage-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa0f6000-7007fa100000 r--s 00000000 fe:00 164 /system/fonts/CarroisGothicSC-Regular.ttf
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa100000-7007fa101000 r--p 00000000 fe:00 1120 /system/lib64/android.hardware.media@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa101000-7007fa102000 r-xp 00001000 fe:00 1120 /system/lib64/android.hardware.media@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa102000-7007fa103000 rw-p 00002000 fe:00 1120 /system/lib64/android.hardware.media@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa103000-7007fa104000 r--p 00003000 fe:00 1120 /system/lib64/android.hardware.media@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa104000-7007fa108000 r--s 00000000 fe:00 167 /system/fonts/NotoSerifArmenian-Bold.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa108000-7007fa10d000 r--s 00000000 fe:00 223 /system/fonts/NotoSerifHebrew-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa10d000-7007fa147000 r--s 00000000 07:08 18 /apex/com.android.tzdata/etc/icu/icu_tzdata.dat
+Size: 232 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 2 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr me ms rr
+7007fa147000-7007fa18a000 r--p 00000000 fe:00 1222 /system/lib64/libsonivox.so
+Size: 268 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 10 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007fa18a000-7007fa19f000 r-xp 00043000 fe:00 1222 /system/lib64/libsonivox.so
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa19f000-7007fa1a0000 rw-p 00058000 fe:00 1222 /system/lib64/libsonivox.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa1a0000-7007fa1a1000 r--p 00059000 fe:00 1222 /system/lib64/libsonivox.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa1a1000-7007fa1a9000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa1a9000-7007fa1ad000 r--s 00000000 fe:00 148 /system/fonts/NotoSerifArmenian-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1ad000-7007fa1b2000 r--s 00000000 fe:00 138 /system/fonts/NotoSansHebrew-Bold.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1b2000-7007fa1d2000 r--s 00000000 00:13 6732 /dev/__properties__/u:object_r:radio_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1d2000-7007fa1d3000 r--p 00000000 fe:00 1629 /system/lib64/libdexfile_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa1d3000-7007fa1d4000 r-xp 00001000 fe:00 1629 /system/lib64/libdexfile_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa1d4000-7007fa1d5000 rw-p 00002000 fe:00 1629 /system/lib64/libdexfile_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa1d5000-7007fa1d6000 r--p 00003000 fe:00 1629 /system/lib64/libdexfile_support.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa1d6000-7007fa1da000 r--s 00000000 fe:00 104 /system/fonts/NotoSansArmenian-Bold.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1da000-7007fa1df000 r--s 00000000 fe:00 158 /system/fonts/NotoSansHebrew-Regular.ttf
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1df000-7007fa1ee000 r--s 00000000 fe:00 192 /system/fonts/ComingSoon.ttf
+Size: 60 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa1ee000-7007fa20e000 rw-p 00000000 00:00 0 [anon:dalvik-LinearAlloc]
+Name: [anon:dalvik-LinearAlloc]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 80 kB
+Pss: 76 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 76 kB
+Referenced: 76 kB
+Anonymous: 80 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 76 kB
+VmFlags: rd wr mr mw me ac
+7007fa20e000-7007fa21e000 r--p 00000000 fe:00 1535 /system/lib64/libmemunreachable.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 56 kB
+Pss: 9 kB
+Shared_Clean: 56 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 56 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007fa21e000-7007fa239000 r-xp 00010000 fe:00 1535 /system/lib64/libmemunreachable.so
+Size: 108 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 100 kB
+Pss: 21 kB
+Shared_Clean: 100 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 100 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 21 kB
+VmFlags: rd ex mr mw me
+7007fa239000-7007fa23a000 rw-p 0002b000 fe:00 1535 /system/lib64/libmemunreachable.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa23a000-7007fa23c000 r--p 0002c000 fe:00 1535 /system/lib64/libmemunreachable.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa23c000-7007fa23d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa23d000-7007fa24e000 r--s 00000000 fe:00 200 /system/fonts/CutiveMono.ttf
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa24e000-7007fa255000 r--p 00000000 fe:00 1174 /system/lib64/libmediaextractor.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 4 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007fa255000-7007fa25c000 r-xp 00007000 fe:00 1174 /system/lib64/libmediaextractor.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 9 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd ex mr mw me
+7007fa25c000-7007fa25d000 rw-p 0000e000 fe:00 1174 /system/lib64/libmediaextractor.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa25d000-7007fa25e000 r--p 0000f000 fe:00 1174 /system/lib64/libmediaextractor.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa25e000-7007fa25f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa25f000-7007fa263000 r--s 00000000 fe:00 255 /system/fonts/NotoSansArmenian-Regular.ttf
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa263000-7007fa264000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa264000-7007fa267000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa267000-7007fa268000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa269000-7007fa26b000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa26b000-7007fa26c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa26c000-7007fa26f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa26f000-7007fa270000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa270000-7007fa271000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007fa271000-7007fa279000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa279000-7007fa27a000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa27a000-7007fa27d000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa27d000-7007fa27e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa27e000-7007fa27f000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007fa27f000-7007fa287000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa287000-7007fa292000 r--p 00000000 fe:00 1524 /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 4 kB
+Shared_Clean: 44 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 44 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007fa292000-7007fa29c000 r-xp 0000b000 fe:00 1524 /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa29c000-7007fa29d000 rw-p 00015000 fe:00 1524 /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa29d000-7007fa29f000 r--p 00016000 fe:00 1524 /system/lib64/android.hardware.graphics.allocator@2.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa29f000-7007fa2a0000 r--s 00000000 fe:00 995 /system/usr/hyphen-data/hyph-la.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa2a0000-7007fa2a1000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa2a1000-7007fa2a3000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa2a4000-7007fa2a6000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2a6000-7007fa2a7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa2a7000-7007fa2aa000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2aa000-7007fa2ab000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa2ab000-7007fa2ac000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007fa2ac000-7007fa2b4000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2b4000-7007fa2b5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa2b5000-7007fa2b8000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2b8000-7007fa2b9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fa2b9000-7007fa2ba000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007fa2ba000-7007fa2c2000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2c2000-7007fa2ee000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 176 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2ee000-7007fa2f2000 r--p 00000000 fe:00 1525 /system/lib64/libheif.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 4 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007fa2f2000-7007fa2f4000 r-xp 00004000 fe:00 1525 /system/lib64/libheif.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa2f4000-7007fa2f5000 rw-p 00006000 fe:00 1525 /system/lib64/libheif.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa2f5000-7007fa2f6000 r--p 00007000 fe:00 1525 /system/lib64/libheif.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa2f6000-7007fa2f7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fa2f7000-7007fa317000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa317000-7007fa32a000 r--p 00000000 fe:00 1186 /system/lib64/libhwbinder.so
+Size: 76 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 2 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007fa32a000-7007fa33b000 r-xp 00013000 fe:00 1186 /system/lib64/libhwbinder.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 2 kB
+Shared_Clean: 68 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 68 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd ex mr mw me
+7007fa33b000-7007fa33c000 rw-p 00024000 fe:00 1186 /system/lib64/libhwbinder.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa33c000-7007fa33e000 r--p 00025000 fe:00 1186 /system/lib64/libhwbinder.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa33e000-7007fa33f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa33f000-7007fa340000 r--s 00000000 fe:00 968 /system/usr/hyphen-data/hyph-kn.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa340000-7007fa348000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa348000-7007fa36b000 r--p 00000000 fe:00 1698 /system/lib64/libunwindstack.so
+Size: 140 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 6 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007fa36b000-7007fa39c000 r-xp 00023000 fe:00 1698 /system/lib64/libunwindstack.so
+Size: 196 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa39c000-7007fa39d000 rw-p 00054000 fe:00 1698 /system/lib64/libunwindstack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa39d000-7007fa3a4000 r--p 00055000 fe:00 1698 /system/lib64/libunwindstack.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 28 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 28 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007fa3a4000-7007fa3a5000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa3a5000-7007fa3a6000 r--s 00000000 fe:00 999 /system/usr/hyphen-data/hyph-hy.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa3a6000-7007fa3e2000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa3e2000-7007fa3e4000 r--p 00000000 fe:00 1236 /system/lib64/libaudiomanager.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 1 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007fa3e4000-7007fa3e5000 r-xp 00002000 fe:00 1236 /system/lib64/libaudiomanager.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa3e5000-7007fa3e6000 rw-p 00003000 fe:00 1236 /system/lib64/libaudiomanager.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa3e6000-7007fa3e7000 r--p 00004000 fe:00 1236 /system/lib64/libaudiomanager.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa3e7000-7007fa3e8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa3e8000-7007fa420000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa420000-7007fa42d000 r--p 00000000 fe:00 1241 /system/lib64/libsensor.so
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 10 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007fa42d000-7007fa434000 r-xp 0000d000 fe:00 1241 /system/lib64/libsensor.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 9 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd ex mr mw me
+7007fa434000-7007fa435000 rw-p 00014000 fe:00 1241 /system/lib64/libsensor.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa435000-7007fa438000 r--p 00015000 fe:00 1241 /system/lib64/libsensor.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa438000-7007fa439000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa439000-7007fa43b000 r--s 00000000 fe:00 82 /system/fonts/NotoSansOsmanya-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa43b000-7007fa477000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa477000-7007fa478000 r--p 00000000 fe:00 1182 /system/lib64/libgraphicsenv.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa478000-7007fa479000 r-xp 00001000 fe:00 1182 /system/lib64/libgraphicsenv.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa479000-7007fa47a000 rw-p 00002000 fe:00 1182 /system/lib64/libgraphicsenv.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa47a000-7007fa47b000 r--p 00003000 fe:00 1182 /system/lib64/libgraphicsenv.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa47b000-7007fa47c000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd wr mr mw me ac
+7007fa47c000-7007fa47d000 r--s 00000000 fe:00 993 /system/usr/hyphen-data/hyph-hr.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa47d000-7007fa485000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa485000-7007fa48c000 r--p 00000000 fe:00 1132 /system/lib64/liblog.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 0 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa48c000-7007fa49d000 r-xp 00007000 fe:00 1132 /system/lib64/liblog.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 1 kB
+Shared_Clean: 68 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 68 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd ex mr mw me
+7007fa49d000-7007fa49e000 rw-p 00018000 fe:00 1132 /system/lib64/liblog.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa49e000-7007fa49f000 r--p 00019000 fe:00 1132 /system/lib64/liblog.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa49f000-7007fa4a0000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa4a0000-7007fa4d8000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa4d8000-7007fa4ec000 r--p 00000000 fe:00 1108 /system/lib64/libdrmframework.so
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 11 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd mr mw me
+7007fa4ec000-7007fa4f8000 r-xp 00014000 fe:00 1108 /system/lib64/libdrmframework.so
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 48 kB
+Pss: 12 kB
+Shared_Clean: 48 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd ex mr mw me
+7007fa4f8000-7007fa4f9000 rw-p 00020000 fe:00 1108 /system/lib64/libdrmframework.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa4f9000-7007fa4fe000 r--p 00021000 fe:00 1108 /system/lib64/libdrmframework.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 20 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 20 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007fa4fe000-7007fa4ff000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa4ff000-7007fa527000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa527000-7007fa540000 r--p 00000000 fe:00 1577 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size: 100 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 9 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007fa540000-7007fa568000 r-xp 00019000 fe:00 1577 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa568000-7007fa569000 rw-p 00041000 fe:00 1577 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa569000-7007fa56d000 r--p 00042000 fe:00 1577 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa56d000-7007fa56f000 r--s 00000000 fe:00 58 /system/fonts/NotoSansOldTurkic-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa56f000-7007fa597000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa597000-7007fa59b000 r--p 00000000 fe:00 1619 /system/lib64/libnativehelper.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 3 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007fa59b000-7007fa59e000 r-xp 00004000 fe:00 1619 /system/lib64/libnativehelper.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa59e000-7007fa59f000 rw-p 00007000 fe:00 1619 /system/lib64/libnativehelper.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa59f000-7007fa5a0000 r--p 00008000 fe:00 1619 /system/lib64/libnativehelper.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa5a0000-7007fa5a1000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa5a1000-7007fa5a3000 r--s 00000000 fe:00 208 /system/fonts/NotoSansOldSouthArabian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa5a3000-7007fa5c3000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa5c3000-7007fa5cc000 r--p 00000000 fe:00 1181 /system/lib64/libimg_utils.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 9 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 9 kB
+VmFlags: rd mr mw me
+7007fa5cc000-7007fa5d4000 r-xp 00009000 fe:00 1181 /system/lib64/libimg_utils.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa5d4000-7007fa5d5000 rw-p 00011000 fe:00 1181 /system/lib64/libimg_utils.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa5d5000-7007fa5d7000 r--p 00012000 fe:00 1181 /system/lib64/libimg_utils.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa5d7000-7007fa5d8000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa5d8000-7007fa5da000 r--s 00000000 fe:00 193 /system/fonts/NotoSansOldItalic-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa5da000-7007fa62e000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 336 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa62e000-7007fa630000 r--p 00000000 fe:00 1251 /system/lib64/libnetd_client.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa630000-7007fa632000 r-xp 00002000 fe:00 1251 /system/lib64/libnetd_client.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa632000-7007fa633000 rw-p 00004000 fe:00 1251 /system/lib64/libnetd_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa633000-7007fa634000 r--p 00005000 fe:00 1251 /system/lib64/libnetd_client.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa634000-7007fa635000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa635000-7007fa636000 r--s 00000000 fe:00 1005 /system/usr/hyphen-data/hyph-hi.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa636000-7007fa64a000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa64a000-7007fa64b000 r--p 00000000 fe:00 1628 /system/lib64/android.hardware.graphics.common@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fa64b000-7007fa64c000 r-xp 00001000 fe:00 1628 /system/lib64/android.hardware.graphics.common@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa64c000-7007fa64d000 rw-p 00002000 fe:00 1628 /system/lib64/android.hardware.graphics.common@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa64d000-7007fa64e000 r--p 00003000 fe:00 1628 /system/lib64/android.hardware.graphics.common@1.1.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa64e000-7007fa696000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 288 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa696000-7007fa6e4000 r--p 00000000 fe:00 1194 /system/lib64/libgui.so
+Size: 312 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 232 kB
+Pss: 39 kB
+Shared_Clean: 232 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 232 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 39 kB
+VmFlags: rd mr mw me
+7007fa6e4000-7007fa72c000 r-xp 0004e000 fe:00 1194 /system/lib64/libgui.so
+Size: 288 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 288 kB
+Pss: 49 kB
+Shared_Clean: 288 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 288 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 49 kB
+VmFlags: rd ex mr mw me
+7007fa72c000-7007fa72d000 rw-p 00096000 fe:00 1194 /system/lib64/libgui.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa72d000-7007fa73e000 r--p 00097000 fe:00 1194 /system/lib64/libgui.so
+Size: 68 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 68 kB
+Pss: 3 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 68 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 48 kB
+Anonymous: 68 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me ac
+7007fa73e000-7007fa73f000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa73f000-7007fa740000 r--s 00000000 fe:00 1004 /system/usr/hyphen-data/hyph-gu.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa740000-7007fa742000 r--s 00000000 fe:00 166 /system/fonts/NotoSansOlChiki-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa742000-7007fa74e000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa74e000-7007fa755000 r--p 00000000 fe:00 1726 /system/lib64/libGLESv1_CM.so
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 28 kB
+Pss: 4 kB
+Shared_Clean: 28 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 28 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me
+7007fa755000-7007fa758000 r-xp 00007000 fe:00 1726 /system/lib64/libGLESv1_CM.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 3 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd ex mr mw me
+7007fa758000-7007fa759000 rw-p 0000a000 fe:00 1726 /system/lib64/libGLESv1_CM.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa759000-7007fa75a000 r--p 0000b000 fe:00 1726 /system/lib64/libGLESv1_CM.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa75a000-7007fa75b000 r--s 00000000 fe:00 1011 /system/usr/hyphen-data/hyph-eu.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa75b000-7007fa75d000 r--s 00000000 fe:00 71 /system/fonts/NotoSansOgham-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa75d000-7007fa785000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa785000-7007fa78a000 r--p 00000000 fe:00 1207 /system/lib64/libcamera_metadata.so
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 2 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007fa78a000-7007fa78d000 r-xp 00005000 fe:00 1207 /system/lib64/libcamera_metadata.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa78d000-7007fa78f000 rw-p 00008000 fe:00 1207 /system/lib64/libcamera_metadata.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa78f000-7007fa790000 r--p 0000a000 fe:00 1207 /system/lib64/libcamera_metadata.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa790000-7007fa791000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa791000-7007fa793000 r--s 00000000 fe:00 88 /system/fonts/NotoSansLydian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa793000-7007fa7db000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 288 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa7db000-7007fa806000 r--p 00000000 fe:00 1248 /system/lib64/libandroidfw.so
+Size: 172 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 128 kB
+Pss: 19 kB
+Shared_Clean: 128 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 128 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 19 kB
+VmFlags: rd mr mw me
+7007fa806000-7007fa839000 r-xp 0002b000 fe:00 1248 /system/lib64/libandroidfw.so
+Size: 204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 200 kB
+Pss: 11 kB
+Shared_Clean: 200 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 200 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 11 kB
+VmFlags: rd ex mr mw me
+7007fa839000-7007fa83a000 rw-p 0005e000 fe:00 1248 /system/lib64/libandroidfw.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa83a000-7007fa83c000 r--p 0005f000 fe:00 1248 /system/lib64/libandroidfw.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa83c000-7007fa83d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa83d000-7007fa83e000 r--s 00000000 fe:00 1027 /system/usr/hyphen-data/hyph-bn.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa83e000-7007fa840000 r--s 00000000 fe:00 87 /system/fonts/NotoSansLycian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa840000-7007fa85c000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa85c000-7007fa88b000 r--p 00000000 fe:00 1556 /system/lib64/android.hardware.media.omx@1.0.so
+Size: 188 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 124 kB
+Pss: 23 kB
+Shared_Clean: 124 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 124 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 23 kB
+VmFlags: rd mr mw me
+7007fa88b000-7007fa8dd000 r-xp 0002f000 fe:00 1556 /system/lib64/android.hardware.media.omx@1.0.so
+Size: 328 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 292 kB
+Pss: 60 kB
+Shared_Clean: 292 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 292 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 60 kB
+VmFlags: rd ex mr mw me
+7007fa8dd000-7007fa8de000 rw-p 00081000 fe:00 1556 /system/lib64/android.hardware.media.omx@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa8de000-7007fa8e6000 r--p 00082000 fe:00 1556 /system/lib64/android.hardware.media.omx@1.0.so
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 32 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 32 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 32 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me ac
+7007fa8e6000-7007fa8e9000 r--s 00000000 fe:00 216 /system/fonts/NotoSansLimbu-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa8e9000-7007fa919000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa919000-7007fa92b000 r--p 00000000 fe:00 1466 /system/lib64/libui.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 6 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd mr mw me
+7007fa92b000-7007fa93d000 r-xp 00012000 fe:00 1466 /system/lib64/libui.so
+Size: 72 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 6 kB
+Shared_Clean: 52 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 52 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 6 kB
+VmFlags: rd ex mr mw me
+7007fa93d000-7007fa93e000 rw-p 00024000 fe:00 1466 /system/lib64/libui.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa93e000-7007fa93f000 r--p 00025000 fe:00 1466 /system/lib64/libui.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa93f000-7007fa940000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fa940000-7007fa94a000 r--p 00000000 fe:00 1667 /system/lib64/android.hardware.memtrack@1.0.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 10 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 10 kB
+VmFlags: rd mr mw me
+7007fa94a000-7007fa953000 r-xp 0000a000 fe:00 1667 /system/lib64/android.hardware.memtrack@1.0.so
+Size: 36 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 36 kB
+Pss: 18 kB
+Shared_Clean: 36 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 36 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 18 kB
+VmFlags: rd ex mr mw me
+7007fa953000-7007fa954000 rw-p 00013000 fe:00 1667 /system/lib64/android.hardware.memtrack@1.0.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa954000-7007fa956000 r--p 00014000 fe:00 1667 /system/lib64/android.hardware.memtrack@1.0.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa956000-7007fa958000 r--s 00000000 fe:00 81 /system/fonts/NotoSansLisu-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa958000-7007fa9a0000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 288 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa9a0000-7007fa9aa000 r--p 00000000 fe:00 1654 /system/lib64/libbacktrace.so
+Size: 40 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 3 kB
+Shared_Clean: 40 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 40 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007fa9aa000-7007fa9ba000 r-xp 0000a000 fe:00 1654 /system/lib64/libbacktrace.so
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa9ba000-7007fa9bb000 rw-p 0001a000 fe:00 1654 /system/lib64/libbacktrace.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa9bb000-7007fa9bc000 r--p 0001b000 fe:00 1654 /system/lib64/libbacktrace.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa9bc000-7007fa9bd000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa9bd000-7007fa9d9000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa9d9000-7007fa9dd000 r--p 00000000 fe:00 1637 /system/lib64/libbpf.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 3 kB
+Shared_Clean: 16 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 16 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007fa9dd000-7007fa9e1000 r-xp 00004000 fe:00 1637 /system/lib64/libbpf.so
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007fa9e1000-7007fa9e2000 rw-p 00008000 fe:00 1637 /system/lib64/libbpf.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fa9e2000-7007fa9e3000 r--p 00009000 fe:00 1637 /system/lib64/libbpf.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fa9e3000-7007fa9e4000 r--s 00000000 fe:00 966 /system/usr/hyphen-data/hyph-bg.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa9e4000-7007fa9e6000 r--s 00000000 fe:00 151 /system/fonts/NotoSansKayahLi-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fa9e6000-7007faa16000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faa16000-7007faa18000 r--p 00000000 fe:00 1465 /system/lib64/libbinderthreadstate.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007faa18000-7007faa1a000 r-xp 00002000 fe:00 1465 /system/lib64/libbinderthreadstate.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 0 kB
+Shared_Clean: 8 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 8 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007faa1a000-7007faa1b000 rw-p 00004000 fe:00 1465 /system/lib64/libbinderthreadstate.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faa1b000-7007faa1c000 r--p 00005000 fe:00 1465 /system/lib64/libbinderthreadstate.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007faa1c000-7007faa1d000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007faa1d000-7007faa59000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 240 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faa59000-7007faa5c000 r--p 00000000 fe:00 1155 /system/lib64/libutilscallstack.so
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 1 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007faa5c000-7007faa5e000 r-xp 00003000 fe:00 1155 /system/lib64/libutilscallstack.so
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007faa5e000-7007faa5f000 rw-p 00005000 fe:00 1155 /system/lib64/libutilscallstack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faa5f000-7007faa60000 r--p 00006000 fe:00 1155 /system/lib64/libutilscallstack.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007faa60000-7007faa61000 r--s 00000000 fe:00 990 /system/usr/hyphen-data/hyph-as.hyb
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faa61000-7007faa63000 r--s 00000000 fe:00 172 /system/fonts/NotoSansInscriptionalParthian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faa63000-7007faa97000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 208 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faa97000-7007faaad000 r--p 00000000 fe:00 1540 /system/lib64/libRScpp.so
+Size: 88 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 12 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd mr mw me
+7007faaad000-7007faadb000 r-xp 00016000 fe:00 1540 /system/lib64/libRScpp.so
+Size: 184 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me
+7007faadb000-7007faadc000 rw-p 00044000 fe:00 1540 /system/lib64/libRScpp.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faadc000-7007faadd000 r--p 00045000 fe:00 1540 /system/lib64/libRScpp.so
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007faadd000-7007faade000 rw-p 00000000 00:00 0 [anon:.bss]
+Name: [anon:.bss]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faade000-7007faaf2000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faaf2000-7007fab32000 rw-p 00000000 00:00 0
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fab32000-7007fab33000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fab33000-7007fab36000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+7007fab36000-7007fab37000 r--s 00046000 fe:00 1943 /system/priv-app/SettingsProvider/SettingsProvider.apk
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 4 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr me ms
+7007fab37000-7007fab38000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fab38000-7007fab3a000 r--s 00000000 fe:00 286 /system/fonts/NotoSansInscriptionalPahlavi-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fab3a000-7007fab52000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fab52000-7007fab53000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fab53000-7007fab54000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fab54000-7007fab55000 rw-p 00000000 00:00 0 [anon:linker_alloc_lob]
+Name: [anon:linker_alloc_lob]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fab55000-7007fab61000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fab61000-7007fab62000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fab62000-7007fab65000 r--s 00000000 fe:00 149 /system/fonts/NotoSansElbasan-Regular.otf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fab65000-7007fab8d000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fab8d000-7007fab8f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fab8f000-7007fab93000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fab93000-7007fab94000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fab94000-7007fab95000 r--s 00000000 fe:30 18 /vendor/overlay/framework-res__auto_generated_rro.apk
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fab95000-7007fab99000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fab99000-7007fab9a000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fab9a000-7007fab9b000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fab9b000-7007fab9c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fab9c000-7007fab9d000 r--s 00004000 fe:30 18 /vendor/overlay/framework-res__auto_generated_rro.apk
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fab9d000-7007fabad000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fabad000-7007fabae000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fabae000-7007fabd6000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 160 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fabd6000-7007fabd7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fabd7000-7007fabda000 r--s 00000000 fe:00 229 /system/fonts/NotoSansDeseret-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fabda000-7007fabee000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fabee000-7007fabef000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fabef000-7007fabf1000 r--s 00000000 fe:00 189 /system/fonts/NotoSansImperialAramaic-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fabf1000-7007fabfd000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fabfd000-7007fabfe000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fabfe000-7007fabff000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fabff000-7007fac37000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fac37000-7007fac39000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fac39000-7007fac3b000 r--s 00000000 fe:00 265 /system/fonts/NotoSansHanunoo-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fac3b000-7007fac57000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fac57000-7007fac58000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fac58000-7007fac5a000 r--s 00000000 fe:00 98 /system/fonts/NotoSansGothic-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fac5a000-7007fac8e000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 208 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fac8e000-7007fac8f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fac8f000-7007fac91000 r--s 00000000 fe:00 114 /system/fonts/NotoSansCypriot-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fac91000-7007faca1000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faca1000-7007faca2000 r--p 00000000 00:00 0 [anon:atexit handlers]
+Name: [anon:atexit handlers]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007faca2000-7007faca3000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007faca3000-7007faca4000 r--s 00000000 fe:10 237571 /data/resource-cache/vendor@overlay@framework-res__auto_generated_rro.apk@idmap
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faca4000-7007faca6000 r--s 00000000 fe:00 61 /system/fonts/NotoSansCarian-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faca6000-7007facb6000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007facb6000-7007facb7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007facb7000-7007facc7000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007facc7000-7007facc8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007facc8000-7007facca000 r--s 00000000 fe:00 102 /system/fonts/NotoSansBuhid-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007facca000-7007facce000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007facce000-7007faccf000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007faccf000-7007face3000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007face3000-7007face4000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007face4000-7007face6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007face6000-7007face8000 r--s 00000000 fe:00 155 /system/fonts/NotoSansBuginese-Regular.ttf
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007face8000-7007fad3c000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 336 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fad3c000-7007fad3d000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fad3d000-7007fad6d000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fad6d000-7007fad6e000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fad6e000-7007fad8a000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fad8a000-7007fad8b000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fad8b000-7007fad8c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fad8c000-7007fada8000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fada8000-7007fada9000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fada9000-7007fadac000 r--s 00000000 fe:00 276 /system/fonts/NotoSansAvestan-Regular.ttf
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fadac000-7007fadc0000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fadc0000-7007fadc1000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fadc1000-7007fadd1000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fadd1000-7007fadd2000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fadd2000-7007fadd3000 r--s 00000000 fe:00 3210 /system/framework/android.test.base.impl.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fadd3000-7007fade7000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fade7000-7007fade8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fade8000-7007fade9000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fade9000-7007fadea000 r--s 00000000 fe:00 3394 /system/framework/framework-oahl-backward-compatibility.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fadea000-7007fae22000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 224 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae22000-7007fae23000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae23000-7007fae24000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fae24000-7007fae3c000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae3c000-7007fae3d000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fae3d000-7007fae3e000 r--s 00000000 fe:00 3144 /system/framework/ims-common.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fae3e000-7007fae3f000 r--s 00000000 fe:00 3155 /system/framework/voip-common.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fae3f000-7007fae43000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae43000-7007fae44000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fae44000-7007fae45000 r--s 00000000 fe:00 3395 /system/framework/telephony-common.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fae45000-7007fae46000 r--s 00000000 fe:00 3158 /system/framework/framework.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fae46000-7007fae47000 r--s 00004000 fe:00 3208 /system/framework/apache-xml.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fae47000-7007fae4b000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae4b000-7007fae4c000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae4c000-7007fae7c000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 192 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fae7c000-7007faebc000 rw-p 00000000 00:00 0 [anon:dalvik-mark stack]
+Name: [anon:dalvik-mark stack]
+Size: 256 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faebc000-7007faebd000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007faebd000-7007faebe000 r--s 00000000 fe:00 3153 /system/framework/bouncycastle.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faebe000-7007faeca000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faeca000-7007faecb000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faecb000-7007faecc000 r--s 00000000 fe:00 3151 /system/framework/okhttp.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faecc000-7007faecd000 r--s 00000000 fe:00 3349 /system/framework/conscrypt.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faecd000-7007faed5000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faed5000-7007faed6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007faed6000-7007faeda000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faeda000-7007faefa000 rw-p 00000000 00:00 0 [anon:dalvik-large marked objects]
+Name: [anon:dalvik-large marked objects]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007faefa000-7007faf1a000 rw-p 00000000 00:00 0 [anon:dalvik-large live objects]
+Name: [anon:dalvik-large live objects]
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faf1a000-7007faf3a000 r--s 00000000 00:13 6709 /dev/__properties__/u:object_r:fingerprint_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 1 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr me ms
+7007faf3a000-7007faf5a000 r--s 00000000 00:13 6747 /dev/__properties__/u:object_r:vold_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faf5a000-7007faf5b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007faf5b000-7007faf5c000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faf5c000-7007faf78000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 112 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faf78000-7007faf98000 r--s 00000000 00:13 6678 /dev/__properties__/u:object_r:config_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faf98000-7007faf99000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faf99000-7007faf9a000 r--s 00000000 fe:00 3157 /system/framework/core-simple.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faf9a000-7007faf9c000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faf9c000-7007fafb0000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fafb0000-7007fafb1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fafb1000-7007fafb3000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fafb3000-7007fafb7000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fafb7000-7007fafd7000 r--s 00000000 00:13 6696 /dev/__properties__/u:object_r:dalvik_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fafd7000-7007fafd8000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fafd8000-7007fafd9000 r--s 00004000 fe:00 3162 /system/framework/core-libart.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fafd9000-7007fafdb000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fafdb000-7007fafe7000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fafe7000-7007fafe8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fafe8000-7007faff8000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faff8000-7007faff9000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faff9000-7007faffa000 r--s 00007000 fe:00 3215 /system/framework/core-oj.jar
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007faffa000-7007faffc000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007faffc000-7007fb014000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 96 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb014000-7007fb034000 r--s 00000000 00:13 6740 /dev/__properties__/u:object_r:system_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb034000-7007fb036000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007fb036000-7007fb042000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb042000-7007fb043000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb043000-7007fb044000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb044000-7007fb045000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb045000-7007fb046000 rw-p 00000000 00:00 0 [anon:dalvik-mod union bitmap]
+Name: [anon:dalvik-mod union bitmap]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb046000-7007fb05a000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 80 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb05a000-7007fb07a000 r--s 00000000 00:13 6717 /dev/__properties__/u:object_r:log_tag_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb07a000-7007fb07b000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb07b000-7007fb07c000 r--p 00000000 00:00 0 [anon:atexit handlers]
+Name: [anon:atexit handlers]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fb07c000-7007fb07e000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb07e000-7007fb093000 r--p 00af1000 fe:00 3165 /system/framework/x86_64/boot-framework.art
+Size: 84 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 64 kB
+Pss: 3 kB
+Shared_Clean: 64 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 64 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 3 kB
+VmFlags: rd mr mw me
+7007fb093000-7007fb094000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb094000-7007fb095000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb095000-7007fb0a5000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 64 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb0a5000-7007fb0c5000 r--s 00000000 00:13 6718 /dev/__properties__/u:object_r:logd_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb0c5000-7007fb0c6000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb0c6000-7007fb0c7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb0c7000-7007fb0c9000 rw-p 00000000 00:00 0 [anon:dalvik-concurrent copying sweep array free buffer]
+Name: [anon:dalvik-concurrent copying sweep array free buffer]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb0c9000-7007fb0d5000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 48 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb0d5000-7007fb0d6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb0d6000-7007fb0de000 rw-p 00000000 00:00 0 [anon:dalvik-thread local mark stack]
+Name: [anon:dalvik-thread local mark stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb0de000-7007fb0fe000 r--s 00000000 00:13 6712 /dev/__properties__/u:object_r:heapprofd_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb0fe000-7007fb11e000 r--s 00000000 00:13 6699 /dev/__properties__/u:object_r:default_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 24 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 24 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 24 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb11e000-7007fb11f000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb11f000-7007fb121000 rw-p 00000000 00:00 0 [anon:dalvik-concurrent copying sweep array free buffer]
+Name: [anon:dalvik-concurrent copying sweep array free buffer]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb121000-7007fb123000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007fb123000-7007fb124000 r--p 00006000 fe:00 3170 /system/framework/x86_64/boot-android.test.base.impl.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb124000-7007fb125000 r--p 00002000 fe:00 3201 /system/framework/x86_64/boot-framework-oahl-backward-compatibility.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb125000-7007fb126000 r--p 0001c000 fe:00 3176 /system/framework/x86_64/boot-ims-common.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb126000-7007fb127000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb127000-7007fb128000 r--p 00011000 fe:00 3198 /system/framework/x86_64/boot-voip-common.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb128000-7007fb12b000 r--p 00116000 fe:00 3178 /system/framework/x86_64/boot-telephony-common.art
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb12b000-7007fb12c000 r--p 0004d000 fe:00 3196 /system/framework/x86_64/boot-ext.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb12c000-7007fb12e000 r--p 00067000 fe:00 3167 /system/framework/x86_64/boot-apache-xml.art
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb12e000-7007fb12f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb12f000-7007fb130000 r--p 00068000 fe:00 3175 /system/framework/x86_64/boot-bouncycastle.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb130000-7007fb131000 r--p 0003d000 fe:00 3166 /system/framework/x86_64/boot-okhttp.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 2 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me
+7007fb131000-7007fb132000 r--p 00040000 fe:00 3168 /system/framework/x86_64/boot-conscrypt.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb132000-7007fb137000 r--p 002d1000 fe:00 3184 /system/framework/x86_64/boot.art
+Size: 20 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 20 kB
+Pss: 1 kB
+Shared_Clean: 20 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 20 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me
+7007fb137000-7007fb13e000 rw-p 00000000 fe:00 944 /system/etc/event-log-tags
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb13e000-7007fb140000 rw-p 00000000 00:00 0 [anon:dalvik-indirect ref table]
+Name: [anon:dalvik-indirect ref table]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb140000-7007fb141000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb141000-7007fb144000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb144000-7007fb145000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb145000-7007fb165000 r--s 00000000 00:13 6697 /dev/__properties__/u:object_r:debug_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb165000-7007fb166000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb166000-7007fb167000 r--p 00001000 fe:00 3197 /system/framework/x86_64/boot-core-simple.art
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb167000-7007fb16a000 r--p 00132000 fe:00 3174 /system/framework/x86_64/boot-core-libart.art
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 12 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me
+7007fb16a000-7007fb16b000 r--s 00000000 00:13 6990 /dev/event-log-tags
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb16b000-7007fb16c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb16c000-7007fb170000 r--p 00000000 00:00 0 [anon:atexit handlers]
+Name: [anon:atexit handlers]
+Size: 16 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 16 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 16 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fb170000-7007fb190000 r--s 00000000 00:13 6750 /dev/__properties__/properties_serial
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb190000-7007fb191000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+Name: [anon:System property context nodes]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb191000-7007fb194000 r--s 00000000 00:13 6672 /dev/__properties__/property_info
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb194000-7007fb195000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb195000-7007fb196000 rw-p 00000000 00:00 0 [anon:arc4random data]
+Name: [anon:arc4random data]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb196000-7007fb197000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb197000-7007fb198000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb198000-7007fb1a5000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 52 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 52 kB
+Pss: 36 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 16 kB
+Private_Clean: 0 kB
+Private_Dirty: 36 kB
+Referenced: 48 kB
+Anonymous: 52 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 36 kB
+VmFlags: rd wr mr mw me ac
+7007fb1a5000-7007fb1c5000 r--s 00000000 00:13 6699 /dev/__properties__/u:object_r:default_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb1c5000-7007fb1c7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd mr mw me ac
+7007fb1c7000-7007fb1c8000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb1c8000-7007fb1e8000 r--s 00000000 00:13 6697 /dev/__properties__/u:object_r:debug_prop:s0
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb1e8000-7007fb1e9000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb1e9000-7007fb1ea000 rw-p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb1ea000-7007fb1eb000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb1eb000-7007fb20b000 r--s 00000000 00:13 6750 /dev/__properties__/properties_serial
+Size: 128 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb20b000-7007fb20c000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+Name: [anon:System property context nodes]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb20c000-7007fb20f000 r--s 00000000 00:13 6672 /dev/__properties__/property_info
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 12 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr me ms
+7007fb20f000-7007fb210000 r--p 00000000 00:00 0 [anon:linker_alloc]
+Name: [anon:linker_alloc]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd mr mw me ac
+7007fb210000-7007fb212000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+Name: [anon:linker_alloc_small_objects]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 8 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 8 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007fb212000-7007fb213000 r--p 00000000 00:00 0 [anon:atexit handlers]
+Name: [anon:atexit handlers]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fb213000-7007fb214000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+Name: [anon:thread signal stack guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me ac
+7007fb214000-7007fb21c000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+Name: [anon:thread signal stack]
+Size: 32 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd wr mr mw me ac
+7007fb21c000-7007fb21d000 rw-p 00000000 00:00 0 [anon:arc4random data]
+Name: [anon:arc4random data]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me ac
+7007fb21d000-7007fb21e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb21e000-7007fb221000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+Name: [anon:bionic TLS]
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007fb221000-7007fb222000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+Name: [anon:bionic TLS guard]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me
+7007fb222000-7007fb267000 r--p 00000000 fe:00 312 /system/bin/linker64
+Size: 276 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 116 kB
+Pss: 1 kB
+Shared_Clean: 116 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 116 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1 kB
+VmFlags: rd mr mw me dw
+7007fb267000-7007fb355000 r-xp 00045000 fe:00 312 /system/bin/linker64
+Size: 952 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 496 kB
+Pss: 7 kB
+Shared_Clean: 496 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 496 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 7 kB
+VmFlags: rd ex mr mw me dw
+7007fb355000-7007fb356000 rw-p 00133000 fe:00 312 /system/bin/linker64
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 4 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 4 kB
+Referenced: 4 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 4 kB
+VmFlags: rd wr mr mw me dw ac
+7007fb356000-7007fb361000 r--p 00134000 fe:00 312 /system/bin/linker64
+Size: 44 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 44 kB
+Pss: 2 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 44 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 12 kB
+Anonymous: 44 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2 kB
+VmFlags: rd mr mw me dw ac
+7007fb361000-7007fb368000 rw-p 00000000 00:00 0
+Size: 28 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 8 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 8 kB
+Referenced: 8 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 8 kB
+VmFlags: rd wr mr mw me ac
+7007fb368000-7007fb369000 r--p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 4 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 4 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr mw me ac
+7007fb369000-7007fb36c000 rw-p 00000000 00:00 0
+Size: 12 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 12 kB
+Pss: 12 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 12 kB
+Referenced: 12 kB
+Anonymous: 12 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 12 kB
+VmFlags: rd wr mr mw me ac
+7ffde4f08000-7ffde4f09000 ---p 00000000 00:00 0
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: mr mw me gd ac
+7ffde4f09000-7ffde5708000 rw-p 00000000 00:00 0 [stack]
+Size: 8188 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 40 kB
+Pss: 32 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 8 kB
+Private_Clean: 0 kB
+Private_Dirty: 32 kB
+Referenced: 36 kB
+Anonymous: 40 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 32 kB
+VmFlags: rd wr mr mw me gd ac
+7ffde5771000-7ffde5773000 r--p 00000000 00:00 0 [vvar]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd mr pf io de dd
+7ffde5773000-7ffde5775000 r-xp 00000000 00:00 0 [vdso]
+Size: 8 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4 kB
+Pss: 0 kB
+Shared_Clean: 4 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex mr mw me de
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex
diff --git a/libmeminfo/testdata1/smaps_short b/libmeminfo/testdata1/smaps_short
new file mode 100644
index 0000000..cee67b3
--- /dev/null
+++ b/libmeminfo/testdata1/smaps_short
@@ -0,0 +1,122 @@
+54c00000-56c00000 r-xp 00000000 00:00 0 [anon:dalvik-zygote-jit-code-cache]
+Name: [anon:dalvik-zygote-jit-code-cache]
+Size: 32768 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 2048 kB
+Pss: 113 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 2048 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 2048 kB
+Anonymous: 2048 kB
+AnonHugePages: 2048 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 113 kB
+VmFlags: rd ex mr mw me ac
+701ea000-70cdb000 rw-p 00000000 fe:00 3165 /system/framework/x86_64/boot-framework.art
+Size: 11204 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 11188 kB
+Pss: 2200 kB
+Shared_Clean: 80 kB
+Shared_Dirty: 9448 kB
+Private_Clean: 0 kB
+Private_Dirty: 1660 kB
+Referenced: 9892 kB
+Anonymous: 11108 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 2200 kB
+VmFlags: rd wr mr mw me ac
+70074dd8d000-70074ee0d000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+Name: [anon:libc_malloc]
+Size: 16896 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 15272 kB
+Pss: 15272 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 15272 kB
+Referenced: 11156 kB
+Anonymous: 15272 kB
+AnonHugePages: 6144 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 15272 kB
+VmFlags: rd wr mr mw me ac
+700755a2d000-700755a6e000 r-xp 00016000 fe:00 1947 /system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex
+Size: 260 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 260 kB
+Pss: 260 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 260 kB
+Private_Dirty: 0 kB
+Referenced: 260 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 260 kB
+VmFlags: rd ex mr mw me
+7007f85b0000-7007f8b9b000 r-xp 001ee000 fe:00 1537 /system/lib64/libhwui.so
+Size: 6060 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 4132 kB
+Pss: 1274 kB
+Shared_Clean: 4132 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 4132 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 1274 kB
+VmFlags: rd ex mr mw me
+ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
+Size: 4 kB
+KernelPageSize: 4 kB
+MMUPageSize: 4 kB
+Rss: 0 kB
+Pss: 0 kB
+Shared_Clean: 0 kB
+Shared_Dirty: 0 kB
+Private_Clean: 0 kB
+Private_Dirty: 0 kB
+Referenced: 0 kB
+Anonymous: 0 kB
+AnonHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+Shared_Hugetlb: 0 kB
+Private_Hugetlb: 0 kB
+Swap: 0 kB
+SwapPss: 0 kB
+Locked: 0 kB
+VmFlags: rd ex
diff --git a/libmeminfo/testdata1/vmallocinfo b/libmeminfo/testdata1/vmallocinfo
new file mode 100644
index 0000000..d48d8bf
--- /dev/null
+++ b/libmeminfo/testdata1/vmallocinfo
@@ -0,0 +1,1774 @@
+0x0000000000000000-0x0000000000000000 69632 of_iomap+0x78/0xb0 phys=17a00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b220000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17c90000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=17ca0000 ioremap
+0x0000000000000000-0x0000000000000000 266240 atomic_pool_init+0x0/0x200 user
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 28672 devm_ioremap_resource+0xd8/0x194 phys=8c0000 ioremap
+0x0000000000000000-0x0000000000000000 28672 devm_ioremap_resource+0xd8/0x194 phys=ac0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=c264000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap_resource+0xd8/0x194 phys=c440000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17980000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 glink_mailbox_probe+0x55c/0xac4 phys=1885000 ioremap
+0x0000000000000000-0x0000000000000000 159744 devm_ioremap_resource+0xd8/0x194 phys=c40a000 ioremap
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 36864 remote_spinlock_init_address+0x1c8/0x224 phys=1f40000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 8192 glink_mailbox_probe+0x634/0xac4 phys=1886000 ioremap
+0x0000000000000000-0x0000000000000000 1052672 of_iomap+0x78/0xb0 phys=17a60000 ioremap
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 73728 msm_watchdog_probe+0x5b8/0xc24 user
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=179e0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=af20000 ioremap
+0x0000000000000000-0x0000000000000000 32768 msm_smem_probe+0x56c/0xdf0 phys=778000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=179e0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=af21000 ioremap
+0x0000000000000000-0x0000000000000000 8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 glink_smem_native_probe+0x42c/0x934 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=85fe0000 ioremap
+0x0000000000000000-0x0000000000000000 40960 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 167936 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 69632 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 69632 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 40960 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=ff1000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=16b000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=18d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=175000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=177000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=10f000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=110000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap_resource+0xd8/0x194 phys=e600000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad06000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad09000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad0a000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad07000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad08000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ad0b000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=af03000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=5091000 ioremap
+0x0000000000000000-0x0000000000000000 8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap
+0x0000000000000000-0x0000000000000000 8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap
+0x0000000000000000-0x0000000000000000 8192 syscon_node_to_regmap+0x198/0x2dc phys=5091000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap
+0x0000000000000000-0x0000000000000000 135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1810000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=ab00000 ioremap
+0x0000000000000000-0x0000000000000000 12288 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c5000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 40960 devm_ioremap+0x84/0xd8 phys=5090000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c9000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150cd000 ioremap
+0x0000000000000000-0x0000000000000000 135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1830000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 20480 msm_bus_noc_qos_init+0x94/0x714 phys=10b8000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150d1000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150d5000 ioremap
+0x0000000000000000-0x0000000000000000 40960 devm_ioremap_resource+0xd8/0x194 phys=5090000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150d9000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1850000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150dd000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150e1000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=150c2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=c222000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=c263000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=c223000 ioremap
+0x0000000000000000-0x0000000000000000 69632 devm_ioremap_resource+0xd8/0x194 phys=ad00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=c265000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap+0x84/0xd8 phys=1c08000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap+0x84/0xd8 phys=1c0a000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=40000000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=40000000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=5091000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap+0x84/0xd8 phys=17d41000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap+0x84/0xd8 phys=17d43000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap+0x84/0xd8 phys=17d45000 ioremap
+0x0000000000000000-0x0000000000000000 8192 syscon_node_to_regmap+0x198/0x2dc phys=17970000 ioremap
+0x0000000000000000-0x0000000000000000 659456 devm_ioremap_resource+0xd8/0x194 phys=e700000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x208/0x77c phys=ae94000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x360/0x77c phys=ae94000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x208/0x77c phys=ae96000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x360/0x77c phys=ae96000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x208/0x77c phys=88ea000 ioremap
+0x0000000000000000-0x0000000000000000 40960 syscon_node_to_regmap+0x198/0x2dc phys=5090000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x360/0x77c phys=88ea000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x428/0x77c phys=88ea000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x48c/0x77c phys=88ea000 ioremap
+0x0000000000000000-0x0000000000000000 135168 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1870000 ioremap
+0x0000000000000000-0x0000000000000000 8192 mdss_pll_probe+0x4f0/0x77c phys=af03000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1d87000 ioremap
+0x0000000000000000-0x0000000000000000 12288 pcpu_alloc+0x3e8/0x9e4 pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap_resource+0xd8/0x194 phys=88e0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_smp2p_probe+0xb0/0x3b0 phys=17990000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_pil_init+0xe4/0x250 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1881000 ioremap
+0x0000000000000000-0x0000000000000000 2101248 msm_smem_probe+0x23c/0xdf0 phys=86000000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=1882000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 69632 devm_ioremap_resource+0xd8/0x194 phys=af00000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=b221000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qsee_ipc_irq_bridge_probe+0x414/0xb0c phys=1888000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_rng_probe+0x90/0x4a8 phys=793000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=ae94000 ioremap
+0x0000000000000000-0x0000000000000000 69632 devm_ioremap_resource+0xd8/0x194 phys=ab00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=ae96000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=898000 ioremap
+0x0000000000000000-0x0000000000000000 24576 diag_dci_init+0x25c/0x35c pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 69632 devm_ioremap_resource+0xd8/0x194 phys=5040000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=ae94000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=a84000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=af08000 ioremap
+0x0000000000000000-0x0000000000000000 20480 msm_dss_ioremap_byname+0x84/0x134 phys=af30000 ioremap
+0x0000000000000000-0x0000000000000000 12288 msm_dss_ioremap_byname+0x84/0x134 phys=af20000 ioremap
+0x0000000000000000-0x0000000000000000 20480 alloc_and_map+0xb0/0x1cc user
+0x0000000000000000-0x0000000000000000 20480 alloc_and_map+0xb0/0x1cc user
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=16e0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 16384 devm_ioremap_resource+0xd8/0x194 phys=1d84000 ioremap
+0x0000000000000000-0x0000000000000000 69632 syscon_node_to_regmap+0x198/0x2dc phys=af00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 16384 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 20480 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 dmam_alloc_coherent+0xb0/0x11c user
+0x0000000000000000-0x0000000000000000 8192 dmam_alloc_coherent+0xb0/0x11c user
+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap_nocache+0x84/0xd8 phys=c300000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=5061000 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 69632 syscon_node_to_regmap+0x198/0x2dc phys=ab00000 ioremap
+0x0000000000000000-0x0000000000000000 65536 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1700000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 16384 devm_ioremap_nocache+0x84/0xd8 phys=88e8000 ioremap
+0x0000000000000000-0x0000000000000000 69632 syscon_node_to_regmap+0x198/0x2dc phys=ad00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 65536 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 20480 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=88e2000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=780000 ioremap
+0x0000000000000000-0x0000000000000000 217088 ipa3_pre_init+0x984/0x20c4 phys=1e40000 ioremap
+0x0000000000000000-0x0000000000000000 20480 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=88e7000 ioremap
+0x0000000000000000-0x0000000000000000 1052672 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1890000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 msm_slim_sps_mem_alloc+0xb8/0x130 user
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 528384 _persistent_ram_buffer_map+0x1ec/0x238 phys=a1990000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 40960 qce_sps_init_ep_conn+0x168/0x348 user
+0x0000000000000000-0x0000000000000000 77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 40960 qce_sps_init_ep_conn+0x168/0x348 user
+0x0000000000000000-0x0000000000000000 40960 qce_sps_init_ep_conn+0x168/0x348 user
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=880000 ioremap
+0x0000000000000000-0x0000000000000000 2035712 devm_ioremap_resource+0xd8/0x194 phys=100000 ioremap
+0x0000000000000000-0x0000000000000000 40960 qce_sps_init_ep_conn+0x168/0x348 user
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=a6f8000 ioremap
+0x0000000000000000-0x0000000000000000 8192 of_iomap+0x78/0xb0 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 12587008 devm_ioremap_resource+0xd8/0x194 phys=3400000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc
+0x0000000000000000-0x0000000000000000 36864 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac40000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1500000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac48000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=890000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acb3000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=14e0000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 77824 sps_bam_pipe_set_params+0x1e4/0x4c8 pages=18 vmalloc
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acba000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=89c000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acc8000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=17900000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac65000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac66000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac67000 ioremap
+0x0000000000000000-0x0000000000000000 36864 devm_ioremap_resource+0xd8/0x194 phys=1d90000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac68000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap+0x84/0xd8 phys=a8c000 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac5a000 ioremap
+0x0000000000000000-0x0000000000000000 16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac18000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 102400 dmam_alloc_coherent+0xb0/0x11c user
+0x0000000000000000-0x0000000000000000 16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac87000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac5b000 ioremap
+0x0000000000000000-0x0000000000000000 16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac91000 ioremap
+0x0000000000000000-0x0000000000000000 36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac6b000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=888000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d78000 ioremap
+0x0000000000000000-0x0000000000000000 16384 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac6f000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d43000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d78000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d70000 ioremap
+0x0000000000000000-0x0000000000000000 36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d45000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=88c000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=17d70000 ioremap
+0x0000000000000000-0x0000000000000000 12288 devm_ioremap_nocache+0x84/0xd8 phys=858b2000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1740000 ioremap
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 135168 crypto_scomp_alloc_scratches+0x74/0xdc pages=32 vmalloc
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=894000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=1436000 ioremap
+0x0000000000000000-0x0000000000000000 16384 devm_ioremap_nocache+0x84/0xd8 phys=14693000 ioremap
+0x0000000000000000-0x0000000000000000 69632 kgsl_iommu_init+0x154/0x5a0 phys=5040000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=1436000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=114a000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=170f7000 ioremap
+0x0000000000000000-0x0000000000000000 36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=170f7000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ebt_register_table+0xc4/0x3d4 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 ebt_register_table+0xd4/0x3d4 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 24576 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac42000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=4080000 ioremap
+0x0000000000000000-0x0000000000000000 33558528 devm_ioremap_resource+0xd8/0x194 phys=c600000 ioremap
+0x0000000000000000-0x0000000000000000 33558528 mem_dump_probe+0x1d4/0x464 user
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=1f63000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=1f65000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=1f64000 ioremap
+0x0000000000000000-0x0000000000000000 36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=4180000 ioremap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acaf000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=c2b0000 ioremap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acb6000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=b2e0000 ioremap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=acc4000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap+0x84/0xd8 phys=4180000 ioremap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac4a000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 28672 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac00000 ioremap
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1620000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 36864 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac10000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac4e000 ioremap
+0x0000000000000000-0x0000000000000000 20480 cam_soc_util_request_platform_resource+0xd4/0x5e8 phys=ac52000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=a88000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_resource+0xd8/0x194 phys=a90000 ioremap
+0x0000000000000000-0x0000000000000000 20480 devm_ioremap_nocache+0x84/0xd8 phys=146bf000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=88ee000 ioremap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_resource+0xd8/0x194 phys=a60c000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init+0x15c/0x5c0 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init+0x1d0/0x5c0 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init+0x2a0/0x5c0 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1380000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 __ipa_commit_hdr_v3_0+0x19c/0xefc user
+0x0000000000000000-0x0000000000000000 20480 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=1740000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_gadget_init_hw_endpoints+0x2a4/0x368 user
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 98304 sps_register_bam_device+0x884/0xd9c phys=a704000 ioremap
+0x0000000000000000-0x0000000000000000 8192 dwc3_core_pre_init+0x438/0x480 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_msm_notify_event+0x704/0xee0 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_msm_notify_event+0x704/0xee0 user
+0x0000000000000000-0x0000000000000000 8192 dwc3_msm_notify_event+0x704/0xee0 user
+0x0000000000000000-0x0000000000000000 200704 devm_ioremap+0x84/0xd8 phys=506a000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 devm_ioremap_nocache+0x84/0xd8 phys=aeac000 ioremap
+0x0000000000000000-0x0000000000000000 16384 devm_ioremap_nocache+0x84/0xd8 phys=aeb0000 ioremap
+0x0000000000000000-0x0000000000000000 528384 devm_ioremap_resource+0xd8/0x194 phys=15000000 ioremap
+0x0000000000000000-0x0000000000000000 24576 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 20480 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 135168 qce_open+0x360/0x1760 phys=1de0000 ioremap
+0x0000000000000000-0x0000000000000000 36864 drm_ht_create+0x50/0x84 pages=8 vmalloc
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 12288 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 45056 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 12288 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 45056 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 12288 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 397312 devm_ioremap_nocache+0x84/0xd8 phys=800000 ioremap
+0x0000000000000000-0x0000000000000000 45056 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 12288 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 45056 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 139264 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 106496 mnh_alloc_coherent+0x94/0xd0 user
+0x0000000000000000-0x0000000000000000 8192 firmware_data_write+0xe8/0x228 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 firmware_loading_store+0x1b8/0x244
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 firmware_data_write+0xe8/0x228 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 firmware_loading_store+0x1b8/0x244
+0x0000000000000000-0x0000000000000000 12288 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 SyS_swapon+0x720/0xd20 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 swap_cgroup_swapon+0x38/0x140 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 528384 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 disksize_store+0x9c/0x154 pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 24576 lz4_init+0x1c/0x48 pages=5 vmalloc
+0x0000000000000000-0x0000000000000000 8192 ipa3_uc_event_handler+0x378/0x6c8 phys=1e47000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_uc_wdi_event_log_info_handler+0x1bc/0x350 phys=1e47000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 110592 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 40960 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 40960 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 249856 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 266240 SyS_swapon+0x650/0xd20 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_multi_pages_alloc+0x388/0x64c [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 20480 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 139264 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 20480 pcpu_alloc+0x3e8/0x9e4 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 20975616 memremap+0x188/0x200 phys=8ab00000 ioremap
+0x0000000000000000-0x0000000000000000 372736 qce_open+0x68c/0x1760 user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 397312 devm_ioremap_nocache+0x84/0xd8 phys=a00000 ioremap
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 12288 lpm_probe+0x194/0x2e4 user
+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap+0x84/0xd8 phys=40100000 ioremap
+0x0000000000000000-0x0000000000000000 1052672 msm_rtb_probe+0x114/0x268 user
+0x0000000000000000-0x0000000000000000 8192 pci_ioremap_bar+0x80/0xb4 phys=41c00000 ioremap
+0x0000000000000000-0x0000000000000000 16384 sde_rot_ioremap_byname+0x84/0x144 phys=aeb8000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 61440 verity_ctr+0x6c4/0x8c4 pages=14 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 266240 devm_ioremap+0x84/0xd8 phys=5000000 ioremap
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 20480 verity_ctr+0x6c4/0x8c4 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 32768 verity_ctr+0x6c4/0x8c4 pages=7 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 alloc_buffer+0x16c/0x208 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 151552 qce_open+0xc08/0x1760 phys=1dc4000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 20480 load_module+0x24ac/0x3408 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 12288 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 dm_create+0x78/0x490 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 4198400 disksize_store+0x9c/0x154 pages=1024 vmalloc vpages
+0x0000000000000000-0x0000000000000000 1052672 devm_ioremap+0x84/0xd8 phys=40200000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 dm_table_create+0x84/0xf0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 135168 qce_open+0x360/0x1760 phys=1de0000 ioremap
+0x0000000000000000-0x0000000000000000 372736 qce_open+0x68c/0x1760 user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 200704 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 kgsl_sharedmem_alloc_contig+0xc4/0x1f8 user
+0x0000000000000000-0x0000000000000000 2035712 syscon_node_to_regmap+0x198/0x2dc phys=100000 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 16384 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 2428928 syscon_node_to_regmap+0x198/0x2dc phys=1100000 ioremap
+0x0000000000000000-0x0000000000000000 1052672 dmam_alloc_coherent+0xb0/0x11c user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 266240 usbpd_create+0x3c/0x824 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 36864 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 36864 alloc_and_map+0xb0/0x1cc user
+0x0000000000000000-0x0000000000000000 12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 3149824 devm_ioremap+0x84/0xd8 phys=b200000 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 12288 kgsl_sharedmem_page_alloc_user+0x114/0x4ec pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 36864 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 20480 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 qdf_mem_alloc_consistent_debug+0xd4/0x148 [wlan] user
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x131c/0x2794 user
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x131c/0x2794 user
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 184320 ngd_slim_probe+0x334/0x9b0 phys=171c0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 176128 ngd_slim_probe+0x378/0x9b0 phys=17184000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 184320 ngd_slim_probe+0x334/0x9b0 phys=17240000 ioremap
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 16384 n_tty_open+0x1c/0xac pages=3 vmalloc
+0x0000000000000000-0x0000000000000000 8192 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 12288 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8392704 devm_ioremap+0x84/0xd8 phys=18800000 ioremap
+0x0000000000000000-0x0000000000000000 2109440 msm_sharedmem_probe+0x1dc/0x354 user
+0x0000000000000000-0x0000000000000000 16384 fastrpc_internal_invoke+0xab8/0x1c50 user
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 28672 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=6 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 drm_property_create_blob+0x44/0xec pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 135168 ngd_slim_probe+0x378/0x9b0 phys=17204000 ioremap
+0x0000000000000000-0x0000000000000000 12288 pcpu_alloc+0x3e8/0x9e4 pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 rpmstats_show+0x78/0x2b0 phys=c3f0000 ioremap
+0x0000000000000000-0x0000000000000000 12288 drm_property_create_blob+0x44/0xec pages=2 vmalloc
+0x0000000000000000-0x0000000000000000 8192 msm_gem_get_vaddr+0xb0/0xf0 vmap
+0x0000000000000000-0x0000000000000000 8192 wlan_logging_sock_init_svc+0xf8/0x4f0 [wlan] pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 8192 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 2101248 devm_ioremap_nocache+0x84/0xd8 phys=aa00000 ioremap
+0x0000000000000000-0x0000000000000000 45056 drm_property_create_blob+0x44/0xec pages=10 vmalloc
+0x0000000000000000-0x0000000000000000 135168 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 135168 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 12288 gpi_alloc_ring+0x238/0x2e0 user
+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_init+0x9e4/0xc7c user
+0x0000000000000000-0x0000000000000000 1024000 devm_ioremap_nocache+0x84/0xd8 phys=a600000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_allocate_dma_task_for_gsi+0xfc/0x318 user
+0x0000000000000000-0x0000000000000000 8192 ipa3_nat_ipv6ct_init_devices+0x160/0x438 user
+0x0000000000000000-0x0000000000000000 536576 devm_ioremap_nocache+0x84/0xd8 phys=ae00000 ioremap
+0x0000000000000000-0x0000000000000000 708608 sde_rot_ioremap_byname+0x84/0x144 phys=ae00000 ioremap
+0x0000000000000000-0x0000000000000000 33558528 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 8392704 pci_ioremap_bar+0x80/0xb4 phys=40800000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_post_init+0x7fc/0x2c98 user
+0x0000000000000000-0x0000000000000000 442368 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 4198400 pci_ioremap_bar+0x80/0xb4 phys=41800000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1052672 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 36864 ipa3_alloc_common_event_ring+0x178/0x24c user
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x131c/0x2794 user
+0x0000000000000000-0x0000000000000000 12288 ipa3_setup_sys_pipe+0x14cc/0x2794 user
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x131c/0x2794 user
+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipa3_setup_sys_pipe+0x14cc/0x2794 user
+0x0000000000000000-0x0000000000000000 20480 ipa3_setup_sys_pipe+0x14cc/0x2794 user
+0x0000000000000000-0x0000000000000000 8192 ipa3_uc_interface_init+0x104/0x390 phys=1e47000 ioremap
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 8192 ipahal_fltrt_allocate_hw_sys_tbl+0x1cc/0x34c user
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 184320 devm_ioremap_nocache+0x84/0xd8 phys=1e04000 ioremap
+0x0000000000000000-0x0000000000000000 266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 2166784 kgsl_page_alloc_map_kernel+0x70/0x108 ioremap
+0x0000000000000000-0x0000000000000000 266240 bpf_map_area_alloc+0x74/0x94 pages=64 vmalloc
+0x0000000000000000-0x0000000000000000 700416 ipa3_qmi_service_init_worker+0x78/0x55c pages=170 vmalloc
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 8192 bpf_prog_alloc+0x70/0xd0 pages=1 vmalloc
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 5246976 removed_alloc+0x2f8/0x3ac phys=8bf00000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1052672 removed_alloc+0x2f8/0x3ac phys=98500000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 3141632 ion_heap_map_kernel+0x108/0x158 vmap
+0x0000000000000000-0x0000000000000000 533729280 devm_ioremap+0x84/0xd8 phys=40300000 ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 1044480 binder_alloc_mmap_handler+0x60/0x1cc ioremap
+0x0000000000000000-0x0000000000000000 10498048 pktlog_alloc_buf+0xc4/0x15c [wlan] pages=2562 vmalloc vpages
+0x0000000000000000-0x0000000000000000 18350080 setup_arch+0x20c/0x668 phys=80080000 vmap
+0x0000000000000000-0x0000000000000000 8388608 setup_arch+0x20c/0x668 phys=81200000 vmap
+0x0000000000000000-0x0000000000000000 6291456 setup_arch+0x20c/0x668 phys=81a00000 vmap
+0x0000000000000000-0x0000000000000000 11005952 setup_arch+0x20c/0x668 phys=82000000 vmap
+0x0000000000000000-0x0000000000000000 8073216 load_module+0x24ac/0x3408 pages=1970 vmalloc vpages
+0x0000000000000000-0x0000000000000000 20480 load_module+0x24ac/0x3408 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 151552 load_module+0x24ac/0x3408 pages=36 vmalloc
+0x0000000000000000-0x0000000000000000 28672 load_module+0x24ac/0x3408 pages=6 vmalloc
+0x0000000000000000-0x0000000000000000 94208 load_module+0x24ac/0x3408 pages=22 vmalloc
+0x0000000000000000-0x0000000000000000 409600 load_module+0x24ac/0x3408 pages=99 vmalloc
+0x0000000000000000-0x0000000000000000 36864 load_module+0x24ac/0x3408 pages=8 vmalloc
+0x0000000000000000-0x0000000000000000 303104 load_module+0x24ac/0x3408 pages=73 vmalloc
+0x0000000000000000-0x0000000000000000 172032 load_module+0x24ac/0x3408 pages=41 vmalloc
+0x0000000000000000-0x0000000000000000 20480 load_module+0x24ac/0x3408 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 20480 load_module+0x24ac/0x3408 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 20480 load_module+0x24ac/0x3408 pages=4 vmalloc
+0x0000000000000000-0x0000000000000000 245760 load_module+0x24ac/0x3408 pages=59 vmalloc
+0x0000000000000000-0x0000000000000000 786432 pcpu_get_vm_areas+0x0/0x800 vmalloc
+0x0000000000000000-0x0000000000000000 786432 pcpu_get_vm_areas+0x0/0x800 vmalloc
diff --git a/libmeminfo/testdata2/mem_used_total b/libmeminfo/testdata2/mem_used_total
new file mode 100644
index 0000000..97fcf41
--- /dev/null
+++ b/libmeminfo/testdata2/mem_used_total
@@ -0,0 +1 @@
+31236096
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
new file mode 100644
index 0000000..3968c09
--- /dev/null
+++ b/libmeminfo/tools/Android.bp
@@ -0,0 +1,90 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+ name: "librank",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["librank.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmeminfo",
+ ],
+}
+
+cc_binary {
+ name: "procmem",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["procmem.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmeminfo",
+ ],
+}
+
+cc_binary {
+ name: "procrank",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["procrank.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmeminfo",
+ ],
+}
+
+cc_binary {
+ name: "showmap",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: ["showmap.cpp"],
+ shared_libs: [
+ "libbase",
+ "libmeminfo",
+ ],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_binary {
+ name: "wsstop",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: ["wsstop.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libmeminfo",
+ ],
+}
diff --git a/libmeminfo/tools/librank.cpp b/libmeminfo/tools/librank.cpp
new file mode 100644
index 0000000..e53c746
--- /dev/null
+++ b/libmeminfo/tools/librank.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+[[noreturn]] static void usage(int exit_status) {
+ fprintf(stderr,
+ "Usage: %s [ -P | -L ] [ -v | -r | -p | -u | -s | -h ]\n"
+ "\n"
+ "Sort options:\n"
+ " -v Sort processes by VSS.\n"
+ " -r Sort processes by RSS.\n"
+ " -p Sort processes by PSS.\n"
+ " -u Sort processes by USS.\n"
+ " -s Sort processes by swap.\n"
+ " (Default sort order is PSS.)\n"
+ " -a Show all mappings, including stack, heap and anon.\n"
+ " -P /path Limit libraries displayed to those in path.\n"
+ " -R Reverse sort order (default is descending).\n"
+ " -m [r][w][x] Only list pages that exactly match permissions\n"
+ " -c Only show cached (storage backed) pages\n"
+ " -C Only show non-cached (ram/swap backed) pages\n"
+ " -k Only show pages collapsed by KSM\n"
+ " -h Display this help screen.\n",
+ getprogname());
+ exit(exit_status);
+}
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+ to->vss += from.vss;
+ to->rss += from.rss;
+ to->pss += from.pss;
+ to->uss += from.uss;
+
+ to->swap += from.swap;
+
+ to->private_clean += from.private_clean;
+ to->private_dirty += from.private_dirty;
+
+ to->shared_clean += from.shared_clean;
+ to->shared_dirty += from.shared_dirty;
+}
+
+struct ProcessRecord {
+ public:
+ ProcessRecord(pid_t pid) : pid_(-1), cmdline_("") {
+ std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+ std::string cmdline;
+ if (!::android::base::ReadFileToString(fname, &cmdline)) {
+ fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+ return;
+ }
+ // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+ // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+ // e.g. xtra-daemon, lowi-server
+ // The .c_str() assignment below then takes care of trimming the cmdline at the first
+ // 0x00. This is how original procrank worked (luckily)
+ cmdline_ = cmdline.c_str();
+ pid_ = pid;
+ usage_.clear();
+ }
+
+ ~ProcessRecord() = default;
+
+ bool valid() const { return pid_ != -1; }
+
+ // Getters
+ pid_t pid() const { return pid_; }
+ const std::string& cmdline() const { return cmdline_; }
+ const MemUsage& usage() const { return usage_; }
+
+ // Add to the usage
+ void AddUsage(const MemUsage& mem_usage) { add_mem_usage(&usage_, mem_usage); }
+
+ private:
+ pid_t pid_;
+ std::string cmdline_;
+ MemUsage usage_;
+};
+
+struct LibRecord {
+ public:
+ LibRecord(const std::string& name) : name_(name) {}
+ ~LibRecord() = default;
+
+ const std::string& name() const { return name_; }
+ const MemUsage& usage() const { return usage_; }
+ const std::map<pid_t, ProcessRecord>& processes() const { return procs_; }
+ uint64_t pss() const { return usage_.pss; }
+ void AddUsage(const ProcessRecord& proc, const MemUsage& mem_usage) {
+ auto [it, inserted] = procs_.insert(std::pair<pid_t, ProcessRecord>(proc.pid(), proc));
+ it->second.AddUsage(mem_usage);
+ add_mem_usage(&usage_, mem_usage);
+ }
+
+ private:
+ std::string name_;
+ MemUsage usage_;
+ std::map<pid_t, ProcessRecord> procs_;
+};
+
+// List of every library / map
+static std::map<std::string, LibRecord> g_libs;
+
+// List of library/map names that we don't want to show by default
+static const std::vector<std::string> g_blacklisted_libs = {"[heap]", "[stack]"};
+
+// Global flags affected by command line
+static uint64_t g_pgflags = 0;
+static uint64_t g_pgflags_mask = 0;
+static uint16_t g_mapflags_mask = 0;
+static bool g_all_libs = false;
+static bool g_has_swap = false;
+static bool g_reverse_sort = false;
+static std::string g_prefix_filter = "";
+
+static bool read_all_pids(std::function<bool(pid_t pid)> for_each_pid) {
+ std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+ if (!procdir) return false;
+
+ struct dirent* dir;
+ pid_t pid;
+ while ((dir = readdir(procdir.get()))) {
+ if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+ if (!for_each_pid(pid)) return false;
+ }
+
+ return true;
+}
+
+static bool scan_libs_per_process(pid_t pid) {
+ ProcMemInfo pmem(pid, false, g_pgflags, g_pgflags_mask);
+ const std::vector<Vma> maps = pmem.Maps();
+ if (maps.size() == 0) {
+ // nothing to do here, continue
+ return true;
+ }
+
+ ProcessRecord proc(pid);
+ if (!proc.valid()) {
+ fprintf(stderr, "Failed to create process record for process: %d\n", pid);
+ return false;
+ }
+
+ for (auto& map : maps) {
+ // skip library / map if prefix for the path doesn't match
+ if (!g_prefix_filter.empty() && !::android::base::StartsWith(map.name, g_prefix_filter)) {
+ continue;
+ }
+ // Skip maps based on map permissions
+ if (g_mapflags_mask &&
+ ((map.flags & (PROT_READ | PROT_WRITE | PROT_EXEC)) != g_mapflags_mask)) {
+ continue;
+ }
+
+ // skip blacklisted library / map names
+ if (!g_all_libs && (std::find(g_blacklisted_libs.begin(), g_blacklisted_libs.end(),
+ map.name) != g_blacklisted_libs.end())) {
+ continue;
+ }
+
+ auto [it, inserted] =
+ g_libs.insert(std::pair<std::string, LibRecord>(map.name, LibRecord(map.name)));
+ it->second.AddUsage(proc, map.usage);
+
+ if (!g_has_swap && map.usage.swap) {
+ g_has_swap = true;
+ }
+ }
+
+ return true;
+}
+
+static uint16_t parse_mapflags(const char* mapflags) {
+ uint16_t ret = 0;
+ for (const char* p = mapflags; *p; p++) {
+ switch (*p) {
+ case 'r':
+ ret |= PROT_READ;
+ break;
+ case 'w':
+ ret |= PROT_WRITE;
+ break;
+ case 'x':
+ ret |= PROT_EXEC;
+ break;
+ default:
+ error(EXIT_FAILURE, 0, "Invalid permissions string: %s, %s", mapflags, p);
+ }
+ }
+
+ return ret;
+}
+
+int main(int argc, char* argv[]) {
+ int opt;
+
+ auto pss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().pss < b.usage().pss : a.usage().pss > b.usage().pss;
+ };
+
+ auto uss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().uss < b.usage().uss : a.usage().uss > b.usage().uss;
+ };
+
+ auto vss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().vss < b.usage().vss : a.usage().vss > b.usage().vss;
+ };
+
+ auto rss_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().rss < b.usage().rss : a.usage().rss > b.usage().rss;
+ };
+
+ auto swap_sort = [](const ProcessRecord& a, const ProcessRecord& b) {
+ return g_reverse_sort ? a.usage().swap < b.usage().swap : a.usage().swap > b.usage().swap;
+ };
+
+ std::function<bool(const ProcessRecord&, const ProcessRecord&)> sort_func = pss_sort;
+
+ while ((opt = getopt(argc, argv, "acChkm:pP:uvrsR")) != -1) {
+ switch (opt) {
+ case 'a':
+ g_all_libs = true;
+ break;
+ case 'c':
+ g_pgflags = 0;
+ g_pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'C':
+ g_pgflags = g_pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ case 'k':
+ g_pgflags = g_pgflags_mask = (1 << KPF_KSM);
+ break;
+ case 'm':
+ g_mapflags_mask = parse_mapflags(optarg);
+ break;
+ case 'p':
+ sort_func = pss_sort;
+ break;
+ case 'P':
+ g_prefix_filter = optarg;
+ break;
+ case 'u':
+ sort_func = uss_sort;
+ break;
+ case 'v':
+ sort_func = vss_sort;
+ break;
+ case 'r':
+ sort_func = rss_sort;
+ break;
+ case 's':
+ sort_func = swap_sort;
+ break;
+ case 'R':
+ g_reverse_sort = true;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (!read_all_pids(scan_libs_per_process)) {
+ error(EXIT_FAILURE, 0, "Failed to read all pids from the system");
+ }
+
+ printf(" %6s %7s %6s %6s %6s ", "RSStot", "VSS", "RSS", "PSS", "USS");
+ if (g_has_swap) {
+ printf(" %6s ", "Swap");
+ }
+ printf("Name/PID\n");
+
+ std::vector<LibRecord> v_libs;
+ v_libs.reserve(g_libs.size());
+ std::transform(g_libs.begin(), g_libs.end(), std::back_inserter(v_libs),
+ [] (std::pair<std::string, LibRecord> const& pair) { return pair.second; });
+
+ // sort the libraries by their pss
+ std::sort(v_libs.begin(), v_libs.end(),
+ [](const LibRecord& l1, const LibRecord& l2) { return l1.pss() > l2.pss(); });
+
+ for (auto& lib : v_libs) {
+ printf("%6" PRIu64 "K %7s %6s %6s %6s ", lib.pss() / 1024, "", "", "", "");
+ if (g_has_swap) {
+ printf(" %6s ", "");
+ }
+ printf("%s\n", lib.name().c_str());
+
+ // sort all mappings first
+
+ std::vector<ProcessRecord> procs;
+ procs.reserve(lib.processes().size());
+ std::transform(lib.processes().begin(), lib.processes().end(), std::back_inserter(procs),
+ [] (std::pair<pid_t, ProcessRecord> const& pair) { return pair.second; });
+
+ std::sort(procs.begin(), procs.end(), sort_func);
+
+ for (auto& p : procs) {
+ const MemUsage& usage = p.usage();
+ printf(" %6s %7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ", "",
+ usage.vss / 1024, usage.rss / 1024, usage.pss / 1024, usage.uss / 1024);
+ if (g_has_swap) {
+ printf("%6" PRIu64 "K ", usage.swap / 1024);
+ }
+ printf(" %s [%d]\n", p.cmdline().c_str(), p.pid());
+ }
+ }
+
+ return 0;
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..47881ed
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <meminfo/procmeminfo.h>
+
+using Vma = ::android::meminfo::Vma;
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+// Global flags to control procmem output
+
+// Set to use page idle bits for working set detection
+bool use_pageidle = false;
+// hides map entries with zero rss
+bool hide_zeroes = false;
+// Reset working set and exit
+bool reset_wss = false;
+// Show working set, mutually exclusive with reset_wss;
+bool show_wss = false;
+
+[[noreturn]] static void usage(int exit_status) {
+ fprintf(stderr,
+ "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+ " -i Uses idle page tracking for working set statistics.\n"
+ " -w Displays statistics for the working set only.\n"
+ " -W Resets the working set of the process.\n"
+ " -p Sort by PSS.\n"
+ " -u Sort by USS.\n"
+ " -m Sort by mapping order (as read from /proc).\n"
+ " -h Hide maps with no RSS.\n",
+ getprogname());
+
+ exit(exit_status);
+}
+
+static void print_separator(std::stringstream& ss) {
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
+ "-------", "-------", "-------", "-------", "-------",
+ "-------", "");
+ return;
+ }
+ ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n", "-------",
+ "-------", "-------", "-------", "-------", "-------",
+ "-------", "-------", "");
+}
+
+static void print_header(std::stringstream& ss) {
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %s\n", "WRss",
+ "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
+ "Name");
+ } else {
+ ss << ::android::base::StringPrintf("%7s %7s %7s %7s %7s %7s %7s %7s %s\n", "Vss",
+ "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi",
+ "Name");
+ }
+ print_separator(ss);
+}
+
+static void print_stats(std::stringstream& ss, const MemUsage& stats) {
+ if (!show_wss) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", stats.vss / 1024);
+ }
+
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64
+ "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ",
+ stats.rss / 1024, stats.pss / 1024, stats.uss / 1024,
+ stats.shared_clean / 1024, stats.shared_dirty / 1024,
+ stats.private_clean / 1024, stats.private_dirty / 1024);
+}
+
+static int show(const MemUsage& proc_stats, const std::vector<Vma>& maps) {
+ std::stringstream ss;
+ print_header(ss);
+ for (auto& vma : maps) {
+ const MemUsage& vma_stats = vma.usage;
+ if (hide_zeroes && vma_stats.rss == 0) {
+ continue;
+ }
+ print_stats(ss, vma_stats);
+ ss << vma.name << std::endl;
+ }
+ print_separator(ss);
+ print_stats(ss, proc_stats);
+ ss << "TOTAL" << std::endl;
+ std::cout << ss.str();
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ int opt;
+ auto pss_sort = [](const Vma& a, const Vma& b) {
+ uint64_t pss_a = a.usage.pss;
+ uint64_t pss_b = b.usage.pss;
+ return pss_a > pss_b;
+ };
+
+ auto uss_sort = [](const Vma& a, const Vma& b) {
+ uint64_t uss_a = a.usage.uss;
+ uint64_t uss_b = b.usage.uss;
+ return uss_a > uss_b;
+ };
+
+ std::function<bool(const Vma& a, const Vma& b)> sort_func = nullptr;
+ while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+ switch (opt) {
+ case 'h':
+ hide_zeroes = true;
+ break;
+ case 'i':
+ // TODO: libmeminfo doesn't support the flag to chose
+ // between idle page tracking vs clear_refs. So for now,
+ // this flag is unused and the library defaults to using
+ // /proc/<pid>/clear_refs for finding the working set.
+ use_pageidle = true;
+ break;
+ case 'm':
+ // this is the default
+ break;
+ case 'p':
+ sort_func = pss_sort;
+ break;
+ case 'u':
+ sort_func = uss_sort;
+ break;
+ case 'W':
+ reset_wss = true;
+ break;
+ case 'w':
+ show_wss = true;
+ break;
+ case '?':
+ usage(EXIT_SUCCESS);
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (optind != (argc - 1)) {
+ fprintf(stderr, "Need exactly one pid at the end\n");
+ usage(EXIT_FAILURE);
+ }
+
+ pid_t pid = atoi(argv[optind]);
+ if (pid == 0) {
+ std::cerr << "Invalid process id" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (reset_wss) {
+ if (!ProcMemInfo::ResetWorkingSet(pid)) {
+ std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+ }
+
+ ProcMemInfo proc(pid, show_wss);
+ const MemUsage& proc_stats = proc.Usage();
+ std::vector<Vma> maps(proc.Maps());
+ if (sort_func != nullptr) {
+ std::sort(maps.begin(), maps.end(), sort_func);
+ }
+
+ return show(proc_stats, maps);
+}
diff --git a/libmeminfo/tools/procrank.cpp b/libmeminfo/tools/procrank.cpp
new file mode 100644
index 0000000..1e44ff9
--- /dev/null
+++ b/libmeminfo/tools/procrank.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <dirent.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <linux/oom.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+using ::android::meminfo::MemUsage;
+using ::android::meminfo::ProcMemInfo;
+
+struct ProcessRecord {
+ public:
+ ProcessRecord(pid_t pid, bool get_wss = false, uint64_t pgflags = 0, uint64_t pgflags_mask = 0)
+ : pid_(-1),
+ oomadj_(OOM_SCORE_ADJ_MAX + 1),
+ cmdline_(""),
+ proportional_swap_(0),
+ unique_swap_(0),
+ zswap_(0) {
+ std::unique_ptr<ProcMemInfo> procmem =
+ std::make_unique<ProcMemInfo>(pid, get_wss, pgflags, pgflags_mask);
+ if (procmem == nullptr) {
+ std::cerr << "Failed to create ProcMemInfo for: " << pid << std::endl;
+ return;
+ }
+
+ std::string fname = ::android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
+ auto oomscore_fp =
+ std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname.c_str(), "re"), fclose};
+ if (oomscore_fp == nullptr) {
+ std::cerr << "Failed to open oom_score_adj file: " << fname << std::endl;
+ return;
+ }
+
+ if (fscanf(oomscore_fp.get(), "%d\n", &oomadj_) != 1) {
+ std::cerr << "Failed to read oomadj from: " << fname << std::endl;
+ return;
+ }
+
+ fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+ if (!::android::base::ReadFileToString(fname, &cmdline_)) {
+ std::cerr << "Failed to read cmdline from: " << fname << std::endl;
+ cmdline_ = "<unknown>";
+ }
+ // We deliberately don't read the proc/<pid>cmdline file directly into 'cmdline_'
+ // because of some processes showing up cmdlines that end with "0x00 0x0A 0x00"
+ // e.g. xtra-daemon, lowi-server
+ // The .c_str() assignment below then takes care of trimming the cmdline at the first
+ // 0x00. This is how original procrank worked (luckily)
+ cmdline_.resize(strlen(cmdline_.c_str()));
+ usage_or_wss_ = get_wss ? procmem->Wss() : procmem->Usage();
+ swap_offsets_ = procmem->SwapOffsets();
+ pid_ = pid;
+ }
+
+ bool valid() const { return pid_ != -1; }
+
+ void CalculateSwap(const uint16_t* swap_offset_array, float zram_compression_ratio) {
+ for (auto& off : swap_offsets_) {
+ proportional_swap_ += getpagesize() / swap_offset_array[off];
+ unique_swap_ += swap_offset_array[off] == 1 ? getpagesize() : 0;
+ zswap_ = proportional_swap_ * zram_compression_ratio;
+ }
+ }
+
+ // Getters
+ pid_t pid() const { return pid_; }
+ const std::string& cmdline() const { return cmdline_; }
+ int32_t oomadj() const { return oomadj_; }
+ uint64_t proportional_swap() const { return proportional_swap_; }
+ uint64_t unique_swap() const { return unique_swap_; }
+ uint64_t zswap() const { return zswap_; }
+
+ // Wrappers to ProcMemInfo
+ const std::vector<uint16_t>& SwapOffsets() const { return swap_offsets_; }
+ const MemUsage& Usage() const { return usage_or_wss_; }
+ const MemUsage& Wss() const { return usage_or_wss_; }
+
+ private:
+ pid_t pid_;
+ int32_t oomadj_;
+ std::string cmdline_;
+ uint64_t proportional_swap_;
+ uint64_t unique_swap_;
+ uint64_t zswap_;
+ MemUsage usage_or_wss_;
+ std::vector<uint16_t> swap_offsets_;
+};
+
+// Show working set instead of memory consumption
+bool show_wss = false;
+// Reset working set of each process
+bool reset_wss = false;
+// Show per-process oom_score_adj column
+bool show_oomadj = false;
+// True if the device has swap enabled
+bool has_swap = false;
+// True, if device has zram enabled
+bool has_zram = false;
+// If zram is enabled, the compression ratio is zram used / swap used.
+float zram_compression_ratio = 0.0;
+// Sort process in reverse, default is descending
+bool reverse_sort = false;
+
+// Calculated total memory usage across all processes in the system
+uint64_t total_pss = 0;
+uint64_t total_uss = 0;
+uint64_t total_swap = 0;
+uint64_t total_pswap = 0;
+uint64_t total_uswap = 0;
+uint64_t total_zswap = 0;
+
+[[noreturn]] static void usage(int exit_status) {
+ std::cerr << "Usage: " << getprogname() << " [ -W ] [ -v | -r | -p | -u | -s | -h ]"
+ << std::endl
+ << " -v Sort by VSS." << std::endl
+ << " -r Sort by RSS." << std::endl
+ << " -p Sort by PSS." << std::endl
+ << " -u Sort by USS." << std::endl
+ << " -s Sort by swap." << std::endl
+ << " (Default sort order is PSS.)" << std::endl
+ << " -R Reverse sort order (default is descending)." << std::endl
+ << " -c Only show cached (storage backed) pages" << std::endl
+ << " -C Only show non-cached (ram/swap backed) pages" << std::endl
+ << " -k Only show pages collapsed by KSM" << std::endl
+ << " -w Display statistics for working set only." << std::endl
+ << " -W Reset working set of all processes." << std::endl
+ << " -o Show and sort by oom score against lowmemorykiller thresholds."
+ << std::endl
+ << " -h Display this help screen." << std::endl;
+ exit(exit_status);
+}
+
+static bool read_all_pids(std::vector<pid_t>* pids, std::function<bool(pid_t pid)> for_each_pid) {
+ pids->clear();
+ std::unique_ptr<DIR, int (*)(DIR*)> procdir(opendir("/proc"), closedir);
+ if (!procdir) return false;
+
+ struct dirent* dir;
+ pid_t pid;
+ while ((dir = readdir(procdir.get()))) {
+ if (!::android::base::ParseInt(dir->d_name, &pid)) continue;
+ if (!for_each_pid(pid)) return false;
+ pids->emplace_back(pid);
+ }
+
+ return true;
+}
+
+static bool count_swap_offsets(const ProcessRecord& proc, uint16_t* swap_offset_array,
+ uint32_t size) {
+ const std::vector<uint16_t>& swp_offs = proc.SwapOffsets();
+ for (auto& off : swp_offs) {
+ if (off >= size) {
+ std::cerr << "swap offset " << off << " is out of bounds for process: " << proc.pid()
+ << std::endl;
+ return false;
+ }
+
+ if (swap_offset_array[off] == USHRT_MAX) {
+ std::cerr << "swap offset " << off << " ref count overflow in process: " << proc.pid()
+ << std::endl;
+ return false;
+ }
+
+ swap_offset_array[off]++;
+ }
+
+ return true;
+}
+
+static void print_header(std::stringstream& ss) {
+ ss.str("");
+ ss << ::android::base::StringPrintf("%5s ", "PID");
+ if (show_oomadj) {
+ ss << ::android::base::StringPrintf("%5s ", "oom");
+ }
+
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s ", "WRss", "WPss", "WUss");
+ // now swap statistics here, working set pages by definition shouldn't end up in swap.
+ } else {
+ ss << ::android::base::StringPrintf("%8s %7s %7s %7s ", "Vss", "Rss", "Pss", "Uss");
+ if (has_swap) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s ", "Swap", "PSwap", "USwap");
+ if (has_zram) {
+ ss << ::android::base::StringPrintf("%7s ", "ZSwap");
+ }
+ }
+ }
+
+ ss << "cmdline";
+}
+
+static void print_process_record(std::stringstream& ss, ProcessRecord& proc) {
+ ss << ::android::base::StringPrintf("%5d ", proc.pid());
+ if (show_oomadj) {
+ ss << ::android::base::StringPrintf("%5d ", proc.oomadj());
+ }
+
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K ",
+ proc.Wss().rss / 1024, proc.Wss().pss / 1024,
+ proc.Wss().uss / 1024);
+ } else {
+ ss << ::android::base::StringPrintf("%7" PRIu64 "K %6" PRIu64 "K %6" PRIu64 "K %6" PRIu64
+ "K ",
+ proc.Usage().vss / 1024, proc.Usage().rss / 1024,
+ proc.Usage().pss / 1024, proc.Usage().uss / 1024);
+ if (has_swap) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", proc.Usage().swap / 1024);
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", proc.proportional_swap() / 1024);
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", proc.unique_swap() / 1024);
+ if (has_zram) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", (proc.zswap() / 1024));
+ }
+ }
+ }
+}
+
+static void print_processes(std::stringstream& ss, std::vector<ProcessRecord>& procs,
+ uint16_t* swap_offset_array) {
+ for (auto& proc : procs) {
+ total_pss += show_wss ? proc.Wss().pss : proc.Usage().pss;
+ total_uss += show_wss ? proc.Wss().uss : proc.Usage().uss;
+ if (!show_wss && has_swap) {
+ proc.CalculateSwap(swap_offset_array, zram_compression_ratio);
+ total_swap += proc.Usage().swap;
+ total_pswap += proc.proportional_swap();
+ total_uswap += proc.unique_swap();
+ if (has_zram) {
+ total_zswap += proc.zswap();
+ }
+ }
+
+ print_process_record(ss, proc);
+ ss << proc.cmdline() << std::endl;
+ }
+}
+
+static void print_separator(std::stringstream& ss) {
+ ss << ::android::base::StringPrintf("%5s ", "");
+ if (show_oomadj) {
+ ss << ::android::base::StringPrintf("%5s ", "");
+ }
+
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s ", "", "------", "------");
+ } else {
+ ss << ::android::base::StringPrintf("%8s %7s %7s %7s ", "", "", "------", "------");
+ if (has_swap) {
+ ss << ::android::base::StringPrintf("%7s %7s %7s ", "------", "------", "------");
+ if (has_zram) {
+ ss << ::android::base::StringPrintf("%7s ", "------");
+ }
+ }
+ }
+
+ ss << ::android::base::StringPrintf("%s", "------");
+}
+
+static void print_totals(std::stringstream& ss) {
+ ss << ::android::base::StringPrintf("%5s ", "");
+ if (show_oomadj) {
+ ss << ::android::base::StringPrintf("%5s ", "");
+ }
+
+ if (show_wss) {
+ ss << ::android::base::StringPrintf("%7s %6" PRIu64 "K %6" PRIu64 "K ", "",
+ total_pss / 1024, total_uss / 1024);
+ } else {
+ ss << ::android::base::StringPrintf("%8s %7s %6" PRIu64 "K %6" PRIu64 "K ", "", "",
+ total_pss / 1024, total_uss / 1024);
+ if (has_swap) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", total_swap / 1024);
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", total_pswap / 1024);
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", total_uswap / 1024);
+ if (has_zram) {
+ ss << ::android::base::StringPrintf("%6" PRIu64 "K ", total_zswap / 1024);
+ }
+ }
+ }
+ ss << "TOTAL";
+}
+
+static void print_sysmeminfo(std::stringstream& ss, ::android::meminfo::SysMemInfo& smi) {
+ if (has_swap) {
+ ss << ::android::base::StringPrintf("ZRAM: %" PRIu64 "K physical used for %" PRIu64
+ "K in swap "
+ "(%" PRIu64 "K total swap)",
+ smi.mem_zram_kb(),
+ (smi.mem_swap_kb() - smi.mem_swap_free_kb()),
+ smi.mem_swap_kb())
+ << std::endl;
+ }
+
+ ss << ::android::base::StringPrintf(" RAM: %" PRIu64 "K total, %" PRIu64 "K free, %" PRIu64
+ "K buffers, "
+ "%" PRIu64 "K cached, %" PRIu64 "K shmem, %" PRIu64
+ "K slab",
+ smi.mem_total_kb(), smi.mem_free_kb(), smi.mem_buffers_kb(),
+ smi.mem_cached_kb(), smi.mem_shmem_kb(), smi.mem_slab_kb());
+}
+
+int main(int argc, char* argv[]) {
+ auto pss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+ MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+ return reverse_sort ? stats_a.pss < stats_b.pss : stats_a.pss > stats_b.pss;
+ };
+
+ auto uss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+ MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+ return reverse_sort ? stats_a.uss < stats_b.uss : stats_a.uss > stats_b.uss;
+ };
+
+ auto rss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+ MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+ return reverse_sort ? stats_a.rss < stats_b.rss : stats_a.rss > stats_b.rss;
+ };
+
+ auto vss_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+ MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+ return reverse_sort ? stats_a.vss < stats_b.vss : stats_a.vss > stats_b.vss;
+ };
+
+ auto swap_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ MemUsage stats_a = show_wss ? a.Wss() : a.Usage();
+ MemUsage stats_b = show_wss ? b.Wss() : b.Usage();
+ return reverse_sort ? stats_a.swap < stats_b.swap : stats_a.swap > stats_b.swap;
+ };
+
+ auto oomadj_sort = [](ProcessRecord& a, ProcessRecord& b) {
+ return reverse_sort ? a.oomadj() < b.oomadj() : a.oomadj() > b.oomadj();
+ };
+
+ // default PSS sort
+ std::function<bool(ProcessRecord & a, ProcessRecord & b)> proc_sort = pss_sort;
+
+ // count all pages by default
+ uint64_t pgflags = 0;
+ uint64_t pgflags_mask = 0;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "cChkoprRsuvwW")) != -1) {
+ switch (opt) {
+ case 'c':
+ pgflags = 0;
+ pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'C':
+ pgflags = (1 << KPF_SWAPBACKED);
+ pgflags_mask = (1 << KPF_SWAPBACKED);
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ case 'k':
+ pgflags = (1 << KPF_KSM);
+ pgflags_mask = (1 << KPF_KSM);
+ break;
+ case 'o':
+ proc_sort = oomadj_sort;
+ show_oomadj = true;
+ break;
+ case 'p':
+ proc_sort = pss_sort;
+ break;
+ case 'r':
+ proc_sort = rss_sort;
+ break;
+ case 'R':
+ reverse_sort = true;
+ break;
+ case 's':
+ proc_sort = swap_sort;
+ break;
+ case 'u':
+ proc_sort = uss_sort;
+ break;
+ case 'v':
+ proc_sort = vss_sort;
+ break;
+ case 'w':
+ show_wss = true;
+ break;
+ case 'W':
+ reset_wss = true;
+ break;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ std::vector<pid_t> pids;
+ std::vector<ProcessRecord> procs;
+ if (reset_wss) {
+ if (!read_all_pids(&pids,
+ [&](pid_t pid) -> bool { return ProcMemInfo::ResetWorkingSet(pid); })) {
+ std::cerr << "Failed to reset working set of all processes" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ // we are done, all other options passed to procrank are ignored in the presence of '-W'
+ return 0;
+ }
+
+ ::android::meminfo::SysMemInfo smi;
+ if (!smi.ReadMemInfo()) {
+ std::cerr << "Failed to get system memory info" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ // Figure out swap and zram
+ uint64_t swap_total = smi.mem_swap_kb() * 1024;
+ has_swap = swap_total > 0;
+ // Allocate the swap array
+ auto swap_offset_array = std::make_unique<uint16_t[]>(swap_total / getpagesize());
+ if (has_swap) {
+ has_zram = smi.mem_zram_kb() > 0;
+ if (has_zram) {
+ zram_compression_ratio = static_cast<float>(smi.mem_zram_kb()) /
+ (smi.mem_swap_kb() - smi.mem_swap_free_kb());
+ }
+ }
+
+ auto mark_swap_usage = [&](pid_t pid) -> bool {
+ ProcessRecord proc(pid, show_wss, pgflags, pgflags_mask);
+ if (!proc.valid()) {
+ // Check to see if the process is still around, skip the process if the proc
+ // directory is inaccessible. It was most likely killed while creating the process
+ // record
+ std::string procdir = ::android::base::StringPrintf("/proc/%d", pid);
+ if (access(procdir.c_str(), F_OK | R_OK)) return true;
+
+ // Warn if we failed to gather process stats even while it is still alive.
+ // Return success here, so we continue to print stats for other processes.
+ std::cerr << "warning: failed to create process record for: " << pid << std::endl;
+ return true;
+ }
+
+ // Skip processes with no memory mappings
+ uint64_t vss = show_wss ? proc.Wss().vss : proc.Usage().vss;
+ if (vss == 0) return true;
+
+ // collect swap_offset counts from all processes in 1st pass
+ if (!show_wss && has_swap &&
+ !count_swap_offsets(proc, swap_offset_array.get(), swap_total / getpagesize())) {
+ std::cerr << "Failed to count swap offsets for process: " << pid << std::endl;
+ return false;
+ }
+
+ procs.emplace_back(std::move(proc));
+ return true;
+ };
+
+ // Get a list of all pids currently running in the system in 1st pass through all processes.
+ // Mark each swap offset used by the process as we find them for calculating proportional
+ // swap usage later.
+ if (!read_all_pids(&pids, mark_swap_usage)) {
+ std::cerr << "Failed to read all pids from the system" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ std::stringstream ss;
+ if (procs.empty()) {
+ // This would happen in corner cases where procrank is being run to find KSM usage on a
+ // system with no KSM and combined with working set determination as follows
+ // procrank -w -u -k
+ // procrank -w -s -k
+ // procrank -w -o -k
+ ss << "<empty>" << std::endl << std::endl;
+ print_sysmeminfo(ss, smi);
+ ss << std::endl;
+ std::cout << ss.str();
+ return 0;
+ }
+
+ // Sort all process records, default is PSS descending
+ std::sort(procs.begin(), procs.end(), proc_sort);
+
+ // start dumping output in string stream
+ print_header(ss);
+ ss << std::endl;
+
+ // 2nd pass to calculate and get per process stats to add them up
+ print_processes(ss, procs, swap_offset_array.get());
+
+ // Add separator to output
+ print_separator(ss);
+ ss << std::endl;
+
+ // Add totals to output
+ print_totals(ss);
+ ss << std::endl << std::endl;
+
+ // Add system information at the end
+ print_sysmeminfo(ss, smi);
+ ss << std::endl;
+
+ // dump on the screen
+ std::cout << ss.str();
+
+ return 0;
+}
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
new file mode 100644
index 0000000..8ea2108
--- /dev/null
+++ b/libmeminfo/tools/showmap.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::Vma;
+
+struct VmaInfo {
+ Vma vma;
+ bool is_bss;
+ uint32_t count;
+
+ VmaInfo() = default;
+ VmaInfo(const Vma& v) : vma(v), is_bss(false), count(1) {}
+ VmaInfo(const Vma& v, bool bss) : vma(v), is_bss(bss), count(1) {}
+ VmaInfo(const Vma& v, const std::string& name, bool bss) : vma(v), is_bss(bss), count(1) {
+ vma.name = name;
+ }
+};
+
+// Global options
+static std::string g_filename = "";
+static bool g_merge_by_names = false;
+static bool g_terse = false;
+static bool g_verbose = false;
+static bool g_show_addr = false;
+static bool g_quiet = false;
+static pid_t g_pid = -1;
+
+static VmaInfo g_total;
+static std::vector<VmaInfo> g_vmas;
+
+[[noreturn]] static void usage(const char* progname, int exit_status) {
+ fprintf(stderr,
+ "%s [-aqtv] [-f FILE] PID\n"
+ "-a\taddresses (show virtual memory map)\n"
+ "-q\tquiet (don't show error if map could not be read)\n"
+ "-t\tterse (show only items with private pages)\n"
+ "-v\tverbose (don't coalesce maps with the same name)\n"
+ "-f\tFILE (read from input from FILE instead of PID)\n",
+ progname);
+
+ exit(exit_status);
+}
+
+static bool is_library(const std::string& name) {
+ return (name.size() > 4) && (name[0] == '/') && ::android::base::EndsWith(name, ".so");
+}
+
+static bool insert_before(const VmaInfo& a, const VmaInfo& b) {
+ if (g_show_addr) {
+ return (a.vma.start < b.vma.start || (a.vma.start == b.vma.start && a.vma.end < b.vma.end));
+ }
+
+ return strcmp(a.vma.name.c_str(), b.vma.name.c_str()) < 0;
+}
+
+static void collect_vma(const Vma& vma) {
+ if (g_vmas.empty()) {
+ g_vmas.emplace_back(vma);
+ return;
+ }
+
+ VmaInfo current(vma);
+ VmaInfo& last = g_vmas.back();
+ // determine if this is bss;
+ if (vma.name.empty()) {
+ if (last.vma.end == current.vma.start && is_library(last.vma.name)) {
+ current.vma.name = last.vma.name;
+ current.is_bss = true;
+ } else {
+ current.vma.name = "[anon]";
+ }
+ }
+
+ std::vector<VmaInfo>::iterator it;
+ for (it = g_vmas.begin(); it != g_vmas.end(); it++) {
+ if (g_merge_by_names && (it->vma.name == current.vma.name)) {
+ it->vma.usage.vss += current.vma.usage.vss;
+ it->vma.usage.rss += current.vma.usage.rss;
+ it->vma.usage.pss += current.vma.usage.pss;
+
+ it->vma.usage.shared_clean += current.vma.usage.shared_clean;
+ it->vma.usage.shared_dirty += current.vma.usage.shared_dirty;
+ it->vma.usage.private_clean += current.vma.usage.private_clean;
+ it->vma.usage.private_dirty += current.vma.usage.private_dirty;
+ it->vma.usage.swap += current.vma.usage.swap;
+ it->vma.usage.swap_pss += current.vma.usage.swap_pss;
+ it->is_bss &= current.is_bss;
+ it->count++;
+ break;
+ }
+
+ if (insert_before(current, *it)) {
+ g_vmas.insert(it, current);
+ break;
+ }
+ }
+
+ if (it == g_vmas.end()) {
+ g_vmas.emplace_back(current);
+ }
+}
+
+static void print_header() {
+ const char* addr1 = g_show_addr ? " start end " : "";
+ const char* addr2 = g_show_addr ? " addr addr " : "";
+
+ printf("%s virtual shared shared private private\n", addr1);
+ printf("%s size RSS PSS clean dirty clean dirty swap swapPSS",
+ addr2);
+ if (!g_verbose && !g_show_addr) {
+ printf(" # ");
+ }
+ printf(" object\n");
+}
+
+static void print_divider() {
+ if (g_show_addr) {
+ printf("-------- -------- ");
+ }
+ printf("-------- -------- -------- -------- -------- -------- -------- -------- -------- ");
+ if (!g_verbose && !g_show_addr) {
+ printf("---- ");
+ }
+ printf("------------------------------\n");
+}
+
+static void print_vmainfo(const VmaInfo& v, bool total) {
+ if (g_show_addr) {
+ if (total) {
+ printf(" ");
+ } else {
+ printf("%16" PRIx64 " %16" PRIx64 " ", v.vma.start, v.vma.end);
+ }
+ }
+ printf("%8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " %8" PRIu64
+ " %8" PRIu64 " %8" PRIu64 " ",
+ v.vma.usage.vss, v.vma.usage.rss, v.vma.usage.pss, v.vma.usage.shared_clean,
+ v.vma.usage.shared_dirty, v.vma.usage.private_clean, v.vma.usage.private_dirty,
+ v.vma.usage.swap, v.vma.usage.swap_pss);
+ if (!g_verbose && !g_show_addr) {
+ printf("%4" PRIu32 " ", v.count);
+ }
+}
+
+static int showmap(void) {
+ if (!::android::meminfo::ForEachVmaFromFile(g_filename, collect_vma)) {
+ if (!g_quiet) {
+ fprintf(stderr, "Failed to parse file %s\n", g_filename.c_str());
+ }
+ return 1;
+ }
+
+ print_header();
+ print_divider();
+
+ for (const auto& v : g_vmas) {
+ g_total.vma.usage.vss += v.vma.usage.vss;
+ g_total.vma.usage.rss += v.vma.usage.rss;
+ g_total.vma.usage.pss += v.vma.usage.pss;
+
+ g_total.vma.usage.private_clean += v.vma.usage.private_clean;
+ g_total.vma.usage.private_dirty += v.vma.usage.private_dirty;
+ g_total.vma.usage.shared_clean += v.vma.usage.shared_clean;
+ g_total.vma.usage.shared_dirty += v.vma.usage.shared_dirty;
+
+ g_total.vma.usage.swap += v.vma.usage.swap;
+ g_total.vma.usage.swap_pss += v.vma.usage.swap_pss;
+ g_total.count += v.count;
+
+ if (g_terse && !(v.vma.usage.private_dirty || v.vma.usage.private_clean)) {
+ continue;
+ }
+
+ print_vmainfo(v, false);
+ printf("%s%s\n", v.vma.name.c_str(), v.is_bss ? " [bss]" : "");
+ }
+
+ print_divider();
+ print_header();
+ print_divider();
+
+ print_vmainfo(g_total, true);
+ printf("TOTAL\n");
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ signal(SIGPIPE, SIG_IGN);
+ struct option longopts[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {0, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "tvaqf:h", longopts, nullptr)) != -1) {
+ switch (opt) {
+ case 't':
+ g_terse = true;
+ break;
+ case 'a':
+ g_show_addr = true;
+ break;
+ case 'v':
+ g_verbose = true;
+ break;
+ case 'q':
+ g_quiet = true;
+ break;
+ case 'f':
+ g_filename = optarg;
+ break;
+ case 'h':
+ usage(argv[0], EXIT_SUCCESS);
+ default:
+ usage(argv[0], EXIT_FAILURE);
+ }
+ }
+
+ if (g_filename.empty()) {
+ if ((argc - 1) < optind) {
+ fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
+ usage(argv[0], EXIT_FAILURE);
+ }
+
+ g_pid = atoi(argv[optind]);
+ if (g_pid <= 0) {
+ fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+ usage(argv[0], EXIT_FAILURE);
+ }
+
+ g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
+ }
+
+ g_merge_by_names = !g_verbose && !g_show_addr;
+ return showmap();
+}
diff --git a/libmeminfo/tools/wsstop.cpp b/libmeminfo/tools/wsstop.cpp
new file mode 100644
index 0000000..368d04e
--- /dev/null
+++ b/libmeminfo/tools/wsstop.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+
+using ::android::meminfo::ProcMemInfo;
+using ::android::meminfo::Vma;
+
+// Global options
+static int32_t g_delay = 0;
+static int32_t g_total = 2;
+static pid_t g_pid = -1;
+
+[[noreturn]] static void usage(int exit_status) {
+ fprintf(stderr,
+ "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n"
+ "-d\tdelay between each working set sample (default 0)\n"
+ "-n\ttotal number of refreshes before we exit (default 2)\n",
+ getprogname());
+
+ exit(exit_status);
+}
+
+static void print_header() {
+ const char* addr1 = " start end ";
+ const char* addr2 = " addr addr ";
+
+ printf("%s virtual shared shared private private\n", addr1);
+ printf("%s size RSS PSS clean dirty clean dirty swap "
+ "swapPSS",
+ addr2);
+ printf(" object\n");
+}
+
+static void print_divider() {
+ printf("---------------- ---------------- ");
+ printf("--------- --------- --------- --------- --------- --------- --------- --------- "
+ "--------- ");
+ printf("------------------------------\n");
+}
+
+static void print_vma(const Vma& v) {
+ printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
+ printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
+ "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
+ v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
+ v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
+ v.usage.swap / 1024, v.usage.swap_pss / 1024);
+ printf("%s\n", v.name.c_str());
+}
+
+static bool same_vma(const Vma& cur, const Vma& last) {
+ return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
+ cur.flags == last.flags && cur.offset == last.offset);
+}
+
+static Vma diff_vma_params(const Vma& cur, const Vma& last) {
+ Vma res;
+ res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
+ ? cur.usage.shared_clean - last.usage.shared_clean
+ : 0;
+ res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
+ ? cur.usage.shared_dirty - last.usage.shared_dirty
+ : 0;
+ res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
+ ? cur.usage.private_clean - last.usage.private_clean
+ : 0;
+ res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
+ ? cur.usage.private_dirty - last.usage.private_dirty
+ : 0;
+
+ res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
+ res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
+ res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
+ res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
+ res.usage.swap_pss =
+ cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;
+
+ // set vma properties to the same as the current one.
+ res.start = cur.start;
+ res.end = cur.end;
+ res.offset = cur.offset;
+ res.flags = cur.flags;
+ res.name = cur.name;
+ return res;
+}
+
+static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
+ res->clear();
+ auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
+ std::sort(wss.begin(), wss.end(), vma_sorter);
+ std::sort(old.begin(), old.end(), vma_sorter);
+ if (old.empty()) {
+ *res = wss;
+ return;
+ }
+
+ for (auto& i : wss) {
+ bool found_same_vma = false;
+ // TODO: This is highly inefficient, fix it if it takes
+ // too long. Worst case will be system_server
+ for (auto& j : old) {
+ if (same_vma(i, j)) {
+ res->emplace_back(diff_vma_params(i, j));
+ found_same_vma = true;
+ break;
+ }
+ }
+
+ if (!found_same_vma) {
+ res->emplace_back(i);
+ }
+ }
+
+ std::sort(res->begin(), res->end(), vma_sorter);
+ return;
+}
+
+static int workingset() {
+ std::vector<Vma> last_wss = {};
+ std::vector<Vma> diff_wss = {};
+ uint32_t nr_refresh = 0;
+
+ while (true) {
+ std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
+ std::vector<Vma> wss = proc_mem->MapsWithPageIdle();
+
+ diff_workingset(wss, last_wss, &diff_wss);
+ diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
+ [](const auto& v) { return v.usage.rss == 0; }),
+ diff_wss.end());
+ if ((nr_refresh % 5) == 0) {
+ print_header();
+ print_divider();
+ }
+
+ for (const auto& v : diff_wss) {
+ print_vma(v);
+ }
+
+ nr_refresh++;
+ if (nr_refresh == g_total) {
+ break;
+ }
+
+ last_wss = wss;
+ sleep(g_delay);
+ print_divider();
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ struct option longopts[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {0, 0, nullptr, 0},
+ };
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
+ switch (opt) {
+ case 'd':
+ g_delay = atoi(optarg);
+ break;
+ case 'n':
+ g_total = atoi(optarg);
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if ((argc - 1) < optind) {
+ fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
+ usage(EXIT_FAILURE);
+ }
+
+ g_pid = atoi(argv[optind]);
+ if (g_pid <= 0) {
+ fprintf(stderr, "Invalid process id %s\n", argv[optind]);
+ usage(EXIT_FAILURE);
+ }
+
+ if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
+ fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
+ return 0;
+ }
+
+ return workingset();
+}
diff --git a/libmeminfo/vts/Android.bp b/libmeminfo/vts/Android.bp
new file mode 100644
index 0000000..5a3a23b
--- /dev/null
+++ b/libmeminfo/vts/Android.bp
@@ -0,0 +1,20 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "vts_meminfo_test",
+ defaults: ["libmeminfo_defaults"],
+ srcs: ["vts_meminfo_test.cpp"],
+ static_libs: ["libmeminfo"],
+}
diff --git a/Android.mk b/libmeminfo/vts/Android.mk
similarity index 73%
copy from Android.mk
copy to libmeminfo/vts/Android.mk
index 7c57258..62d68d9 100644
--- a/Android.mk
+++ b/libmeminfo/vts/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-LOCAL_PATH := $(my-dir)
-include $(call first-makefiles-under,$(LOCAL_PATH))
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := VtsKernelMemInfoTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libmeminfo/vts/AndroidTest.xml b/libmeminfo/vts/AndroidTest.xml
new file mode 100644
index 0000000..9614025
--- /dev/null
+++ b/libmeminfo/vts/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VTS VtsKernelMemInfoTest.">
+ <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsKernelMemInfoTest"/>
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_meminfo_test/vts_meminfo_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_meminfo_test/vts_meminfo_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="precondition-first-api-level" value="29" />
+ <option name="test-timeout" value="10m"/>
+ </test>
+</configuration>
diff --git a/libutils/tests/Mutex_test.cpp b/libmeminfo/vts/vts_meminfo_test.cpp
similarity index 61%
copy from libutils/tests/Mutex_test.cpp
copy to libmeminfo/vts/vts_meminfo_test.cpp
index 8a1805f..3193c31 100644
--- a/libutils/tests/Mutex_test.cpp
+++ b/libmeminfo/vts/vts_meminfo_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,18 @@
* limitations under the License.
*/
-#include <utils/Mutex.h>
-
#include <gtest/gtest.h>
-static android::Mutex mLock;
-static int i GUARDED_BY(mLock);
+#include <meminfo/procmeminfo.h>
-void modifyLockedVariable() REQUIRES(mLock) {
- i = 1;
+namespace android {
+namespace meminfo {
+
+// /proc/<pid>/smaps_rollup support is required.
+TEST(SmapsRollup, IsSupported) {
+ // Use init's pid for this test since it's the only known pid.
+ ASSERT_TRUE(IsSmapsRollupSupported(1));
}
-TEST(Mutex, compile) {
- android::Mutex::Autolock _l(mLock);
- i = 0;
- modifyLockedVariable();
-}
\ No newline at end of file
+} // namespace meminfo
+} // namespace android
diff --git a/libmemtrack/.clang-format b/libmemtrack/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmemtrack/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 0955633..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -28,10 +28,11 @@
cc_binary {
name: "memtrack_test",
- srcs: ["memtrack_test.c"],
+ srcs: ["memtrack_test.cpp"],
+ static_libs: ["libc++fs"],
shared_libs: [
+ "libbase",
"libmemtrack",
- "libpagemap",
],
cflags: [
"-Wall",
diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c
deleted file mode 100644
index 77c935e..0000000
--- a/libmemtrack/memtrack_test.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <memtrack/memtrack.h>
-
-#include <pagemap/pagemap.h>
-
-#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y))
-
-static int getprocname(pid_t pid, char *buf, int len) {
- char *filename;
- FILE *f;
- int rc = 0;
- static const char* unknown_cmdline = "<unknown>";
-
- if (len <= 0) {
- return -1;
- }
-
- if (asprintf(&filename, "/proc/%d/cmdline", pid) < 0) {
- rc = 1;
- goto exit;
- }
-
- f = fopen(filename, "r");
- if (f == NULL) {
- rc = 2;
- goto releasefilename;
- }
-
- if (fgets(buf, len, f) == NULL) {
- rc = 3;
- goto closefile;
- }
-
-closefile:
- (void) fclose(f);
-releasefilename:
- free(filename);
-exit:
- if (rc != 0) {
- /*
- * The process went away before we could read its process name. Try
- * to give the user "<unknown>" here, but otherwise they get to look
- * at a blank.
- */
- if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) {
- rc = 4;
- }
- }
-
- return rc;
-}
-
-int main(int argc, char *argv[])
-{
- int ret;
- pm_kernel_t *ker;
- size_t num_procs;
- pid_t *pids;
- struct memtrack_proc *p;
- size_t i;
-
- (void)argc;
- (void)argv;
-
- ret = pm_kernel_create(&ker);
- if (ret) {
- fprintf(stderr, "Error creating kernel interface -- "
- "does this kernel have pagemap?\n");
- exit(EXIT_FAILURE);
- }
-
- ret = pm_kernel_pids(ker, &pids, &num_procs);
- if (ret) {
- fprintf(stderr, "Error listing processes.\n");
- exit(EXIT_FAILURE);
- }
-
- p = memtrack_proc_new();
- if (ret) {
- fprintf(stderr, "failed to create memtrack process handle\n");
- exit(EXIT_FAILURE);
- }
-
- for (i = 0; i < num_procs; i++) {
- pid_t pid = pids[i];
- char cmdline[256];
- size_t v1;
- size_t v2;
- size_t v3;
- size_t v4;
- size_t v5;
- size_t v6;
-
- getprocname(pid, cmdline, (int)sizeof(cmdline));
-
- ret = memtrack_proc_get(p, pid);
- if (ret) {
- fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n",
- pid, strerror(-ret), ret);
- continue;
- }
-
- v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
- v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
- v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
- v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
- v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
- v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
-
- if (v1 | v2 | v3 | v4 | v5 | v6) {
- printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid,
- v1, v2, v3, v4, v5, v6, cmdline);
- }
- }
-
- memtrack_proc_destroy(p);
-
- return 0;
-}
diff --git a/libmemtrack/memtrack_test.cpp b/libmemtrack/memtrack_test.cpp
new file mode 100644
index 0000000..aeeaf24
--- /dev/null
+++ b/libmemtrack/memtrack_test.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <filesystem>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <memtrack/memtrack.h>
+
+#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))
+
+static void getprocname(pid_t pid, std::string* name) {
+ std::string fname = ::android::base::StringPrintf("/proc/%d/cmdline", pid);
+ if (!::android::base::ReadFileToString(fname, name)) {
+ fprintf(stderr, "Failed to read cmdline from: %s\n", fname.c_str());
+ *name = "<unknown>";
+ }
+}
+
+int main(int /* argc */, char** /* argv */) {
+ int ret;
+ struct memtrack_proc* p;
+ std::vector<pid_t> pids;
+
+ p = memtrack_proc_new();
+ if (p == nullptr) {
+ fprintf(stderr, "failed to create memtrack process handle\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (auto& de : std::filesystem::directory_iterator("/proc")) {
+ if (!std::filesystem::is_directory(de.status())) {
+ continue;
+ }
+
+ pid_t pid;
+ if (!::android::base::ParseInt(de.path().filename().string(), &pid)) {
+ continue;
+ }
+ pids.emplace_back(pid);
+ }
+
+ for (auto& pid : pids) {
+ size_t v1;
+ size_t v2;
+ size_t v3;
+ size_t v4;
+ size_t v5;
+ size_t v6;
+ std::string cmdline;
+
+ getprocname(pid, &cmdline);
+
+ ret = memtrack_proc_get(p, pid);
+ if (ret) {
+ fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", pid, strerror(-ret),
+ ret);
+ continue;
+ }
+
+ v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024);
+ v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024);
+ v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024);
+ v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024);
+ v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024);
+ v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024);
+
+ if (v1 | v2 | v3 | v4 | v5 | v6) {
+ fprintf(stdout, "%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, v1, v2, v3, v4, v5, v6,
+ cmdline.c_str());
+ }
+ }
+
+ memtrack_proc_destroy(p);
+
+ return ret;
+}
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index 248a9d2..62a7266 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -22,6 +22,7 @@
cc_library {
name: "libmemunreachable",
+ vendor_available: true,
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
@@ -46,18 +47,25 @@
static_libs: ["libunwind_llvm"],
},
},
-
- // TODO(b/78118944), clang lld link flags do not work with special link
- // rules for libunwind_llvm yet. Linked aosp_arm-eng image failed to
- // boot up in the emulator.
- use_clang_lld: false,
-
export_include_dirs: ["include"],
local_include_dirs: ["include"],
+ version_script: "libmemunreachable.map",
+}
+
+// Integration test that runs against the public API of libmemunreachable
+cc_test {
+ name: "memunreachable_test",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "tests/MemUnreachable_test.cpp",
+ ],
+ shared_libs: ["libmemunreachable"],
+
+ test_suites: ["device-tests"],
}
cc_test {
- name: "memunreachable_test",
+ name: "memunreachable_unit_test",
defaults: ["libmemunreachable_defaults"],
host_supported: true,
srcs: [
@@ -73,8 +81,9 @@
"tests/MemUnreachable_test.cpp",
"tests/ThreadCapture_test.cpp",
],
- shared_libs: [
+ static_libs: [
"libmemunreachable",
+ "libc_malloc_debug_backtrace",
],
},
host: {
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 2403ad0..7cae048 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -35,6 +35,13 @@
end = begin + 1;
}
Range range{begin, end};
+ if (valid_mappings_range_.end != 0 &&
+ (begin < valid_mappings_range_.begin || end > valid_mappings_range_.end)) {
+ MEM_LOG_ALWAYS_FATAL("allocation %p-%p is outside mapping range %p-%p",
+ reinterpret_cast<void*>(begin), reinterpret_cast<void*>(end),
+ reinterpret_cast<void*>(valid_mappings_range_.begin),
+ reinterpret_cast<void*>(valid_mappings_range_.end));
+ }
auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
if (inserted.second) {
valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
@@ -52,12 +59,19 @@
}
}
+// Sanitizers may consider certain memory inaccessible through certain pointers.
+// With MTE this will need to use unchecked instructions or disable tag checking globally.
+static uintptr_t ReadWordAtAddressUnsafe(uintptr_t word_ptr)
+ __attribute__((no_sanitize("address", "hwaddress"))) {
+ return *reinterpret_cast<uintptr_t*>(word_ptr);
+}
+
bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
walking_ptr_ = word_ptr;
// This access may segfault if the process under test has done something strange,
// for example mprotect(PROT_NONE) on a native heap page. If so, it will be
// caught and handled by mmaping a zero page over the faulting page.
- uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
+ uintptr_t value = ReadWordAtAddressUnsafe(word_ptr);
walking_ptr_ = 0;
if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
@@ -76,15 +90,22 @@
Range range = to_do.back();
to_do.pop_back();
+ walking_range_ = range;
ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
if (!ref_info->referenced_from_root) {
ref_info->referenced_from_root = true;
to_do.push_back(ref_range);
}
});
+ walking_range_ = Range{0, 0};
}
}
+void HeapWalker::Mapping(uintptr_t begin, uintptr_t end) {
+ valid_mappings_range_.begin = std::min(valid_mappings_range_.begin, begin);
+ valid_mappings_range_.end = std::max(valid_mappings_range_.end, end);
+}
+
void HeapWalker::Root(uintptr_t begin, uintptr_t end) {
roots_.push_back(Range{begin, end});
}
@@ -113,6 +134,10 @@
RecurseRoot(vals);
+ if (segv_page_count_ > 0) {
+ MEM_ALOGE("%zu pages skipped due to segfaults", segv_page_count_);
+ }
+
return true;
}
@@ -168,12 +193,20 @@
handler.reset();
return;
}
- MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ if (!segv_logged_) {
+ MEM_ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
+ if (walking_range_.begin != 0U) {
+ MEM_ALOGW("while walking range %p-%p", reinterpret_cast<void*>(walking_range_.begin),
+ reinterpret_cast<void*>(walking_range_.end));
+ }
+ segv_logged_ = true;
+ }
+ segv_page_count_++;
if (!MapOverPage(si->si_addr)) {
handler.reset();
}
}
-ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+Allocator<ScopedSignalHandler::SignalFnMap>::unique_ptr ScopedSignalHandler::handler_map_;
} // namespace android
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 92a8325..f00bcca 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -52,19 +52,30 @@
allocation_bytes_(0),
roots_(allocator),
root_vals_(allocator),
- segv_handler_(),
- walking_ptr_(0) {
+ sigsegv_handler_(allocator),
+ sigbus_handler_(allocator),
+ walking_ptr_(0),
+ walking_range_{0, 0},
+ segv_logged_(false),
+ segv_page_count_(0) {
valid_allocations_range_.end = 0;
valid_allocations_range_.begin = ~valid_allocations_range_.end;
+ valid_mappings_range_.end = 0;
+ valid_mappings_range_.begin = ~valid_allocations_range_.end;
- segv_handler_.install(
+ sigsegv_handler_.install(
SIGSEGV, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
this->HandleSegFault(handler, signal, siginfo, uctx);
});
+ sigbus_handler_.install(
+ SIGBUS, [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
+ this->HandleSegFault(handler, signal, siginfo, uctx);
+ });
}
~HeapWalker() {}
bool Allocation(uintptr_t begin, uintptr_t end);
+ void Mapping(uintptr_t begin, uintptr_t end);
void Root(uintptr_t begin, uintptr_t end);
void Root(const allocator::vector<uintptr_t>& vals);
@@ -95,12 +106,17 @@
AllocationMap allocations_;
size_t allocation_bytes_;
Range valid_allocations_range_;
+ Range valid_mappings_range_;
allocator::vector<Range> roots_;
allocator::vector<uintptr_t> root_vals_;
- ScopedSignalHandler segv_handler_;
- uintptr_t walking_ptr_;
+ ScopedSignalHandler sigsegv_handler_;
+ ScopedSignalHandler sigbus_handler_;
+ volatile uintptr_t walking_ptr_;
+ Range walking_range_;
+ bool segv_logged_;
+ size_t segv_page_count_;
};
template <class F>
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index b160de9..ce937fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -87,6 +87,11 @@
const allocator::vector<Mapping>& mappings,
const allocator::vector<uintptr_t>& refs) {
MEM_ALOGI("searching process %d for allocations", pid_);
+
+ for (auto it = mappings.begin(); it != mappings.end(); it++) {
+ heap_walker_.Mapping(it->begin, it->end);
+ }
+
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
allocator::vector<Mapping> globals_mappings{mappings};
@@ -212,6 +217,10 @@
return ret == 0;
}
+static bool is_sanitizer_mapping(const allocator::string& s) {
+ return s == "[anon:low shadow]" || s == "[anon:high shadow]" || has_prefix(s, "[anon:hwasan");
+}
+
bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings,
allocator::vector<Mapping>& heap_mappings,
allocator::vector<Mapping>& anon_mappings,
@@ -253,7 +262,8 @@
} else if (mapping_name.size() == 0) {
globals_mappings.emplace_back(*it);
} else if (has_prefix(mapping_name, "[anon:") &&
- mapping_name != "[anon:leak_detector_malloc]") {
+ mapping_name != "[anon:leak_detector_malloc]" &&
+ !is_sanitizer_mapping(mapping_name)) {
// TODO(ccross): it would be nice to treat named anonymous mappings as
// possible leaks, but naming something in a .bss or .data section makes
// it impossible to distinguish them from mmaped and then named mappings.
@@ -270,6 +280,12 @@
}
bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
+ if (info.version > 0) {
+ MEM_ALOGE("unsupported UnreachableMemoryInfo.version %zu in GetUnreachableMemory",
+ info.version);
+ return false;
+ }
+
int parent_pid = getpid();
int parent_tid = gettid();
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 701ce16..8e1be4c 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <string.h>
+#include <sys/types.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
@@ -30,7 +31,8 @@
struct ReadMapCallback {
ReadMapCallback(allocator::vector<Mapping>& mappings) : mappings_(mappings) {}
- void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) const {
+ void operator()(uint64_t start, uint64_t end, uint16_t flags, uint64_t, ino_t,
+ const char* name) const {
mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
name);
}
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
index 9e08a8e..ef4473f 100644
--- a/libmemunreachable/ScopedSignalHandler.h
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -24,6 +24,7 @@
#include "android-base/macros.h"
+#include "Allocator.h"
#include "log.h"
namespace android {
@@ -32,17 +33,29 @@
public:
using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;
- explicit ScopedSignalHandler() : signal_(-1) {}
+ explicit ScopedSignalHandler(Allocator<ScopedSignalHandler> allocator) : signal_(-1) {
+ if (handler_map_ == nullptr) {
+ Allocator<SignalFnMap> map_allocator = allocator;
+ handler_map_ = map_allocator.make_unique(allocator);
+ }
+ }
~ScopedSignalHandler() { reset(); }
template <class F>
void install(int signal, F&& f) {
if (signal_ != -1) MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed");
- handler_ = SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
+ if (handler_map_->find(signal) != handler_map_->end()) {
+ MEM_LOG_ALWAYS_FATAL("ScopedSignalHandler already installed for %d", signal);
+ }
+
+ (*handler_map_)[signal] =
+ SignalFn([=](int signal, siginfo_t* si, void* uctx) { f(*this, signal, si, uctx); });
struct sigaction act {};
- act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) { handler_(signal, si, uctx); };
+ act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
+ ((*handler_map_)[signal])(signal, si, uctx);
+ };
act.sa_flags = SA_SIGINFO;
int ret = sigaction(signal, &act, &old_act_);
@@ -59,19 +72,22 @@
if (ret < 0) {
MEM_ALOGE("failed to uninstall segfault handler");
}
- handler_ = SignalFn{};
+
+ handler_map_->erase(signal_);
+ if (handler_map_->empty()) {
+ handler_map_.reset();
+ }
signal_ = -1;
}
}
private:
using SignalFn = std::function<void(int, siginfo_t*, void*)>;
+ using SignalFnMap = allocator::unordered_map<int, SignalFn>;
DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
int signal_;
struct sigaction old_act_;
- // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
- // to be a static map of signals to handlers, but allocated with Allocator.
- static SignalFn handler_;
+ static Allocator<SignalFnMap>::unique_ptr handler_map_;
};
} // namespace android
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index c028eab..011443f 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -28,38 +28,45 @@
namespace android {
struct Leak {
- uintptr_t begin;
- size_t size;
+ uintptr_t begin = 0;
+ size_t size = 0;
- size_t referenced_count;
- size_t referenced_size;
+ size_t referenced_count = 0;
+ size_t referenced_size = 0;
- size_t similar_count;
- size_t similar_size;
- size_t similar_referenced_count;
- size_t similar_referenced_size;
+ size_t similar_count = 0;
+ size_t similar_size = 0;
+ size_t similar_referenced_count = 0;
+ size_t similar_referenced_size = 0;
- size_t total_size;
+ size_t total_size = 0;
static const size_t contents_length = 32;
- char contents[contents_length];
+ char contents[contents_length] = {};
struct Backtrace {
- size_t num_frames;
+ size_t num_frames = 0;
static const size_t max_frames = 16;
- uintptr_t frames[max_frames];
+ uintptr_t frames[max_frames] = {};
+
+ size_t reserved[8] = {};
} backtrace;
+ size_t reserved[8] = {};
+
std::string ToString(bool log_contents) const;
};
struct UnreachableMemoryInfo {
std::vector<Leak> leaks;
- size_t num_leaks;
- size_t leak_bytes;
- size_t num_allocations;
- size_t allocation_bytes;
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ size_t num_allocations = 0;
+ size_t allocation_bytes = 0;
+
+ size_t version = 0; // Must be 0
+ size_t reserved[8] = {};
UnreachableMemoryInfo() {}
~UnreachableMemoryInfo();
diff --git a/libmemunreachable/libmemunreachable.map b/libmemunreachable/libmemunreachable.map
new file mode 100644
index 0000000..0d0d954
--- /dev/null
+++ b/libmemunreachable/libmemunreachable.map
@@ -0,0 +1,13 @@
+LIBMEMUNREACHABLE {
+ global:
+ LogUnreachableMemory;
+ NoLeaks;
+ extern "C++" {
+ android::GetUnreachableMemory*;
+ android::GetUnreachableMemoryString*;
+ android::Leak::*;
+ android::UnreachableMemoryInfo::*;
+ };
+ local:
+ *;
+};
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 84a0ec6..9610cd6 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -73,6 +73,24 @@
ASSERT_FALSE(heap_walker.Allocation(2, 3));
}
+TEST_F(HeapWalkerTest, mapping) {
+ HeapWalker heap_walker(heap_);
+ heap_walker.Mapping(2, 3);
+ heap_walker.Mapping(4, 5);
+ ASSERT_TRUE(heap_walker.Allocation(2, 3));
+ ASSERT_TRUE(heap_walker.Allocation(4, 5));
+ // space between mappings is not checked, but could be in the future
+ ASSERT_TRUE(heap_walker.Allocation(3, 4));
+
+ // re-enable malloc, ASSERT_DEATH may allocate
+ disable_malloc_.Enable();
+ ASSERT_DEATH({ heap_walker.Allocation(1, 2); }, "0x1-0x2.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(1, 3); }, "0x1-0x3.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(4, 6); }, "0x4-0x6.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(5, 6); }, "0x5-0x6.*outside.*0x2-0x5");
+ ASSERT_DEATH({ heap_walker.Allocation(1, 6); }, "0x1-0x6.*outside.*0x2-0x5");
+}
+
#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(buffer)
#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(buffer) + sizeof(buffer))
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index bba0c6d..9cb1623 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -47,7 +47,8 @@
// Trick the compiler into thinking a value on the stack is still referenced.
static void Ref(void** ptr) {
- write(0, ptr, 0);
+ void** volatile storage;
+ storage = ptr;
}
class MemunreachableTest : public ::testing::Test {
@@ -264,4 +265,12 @@
ASSERT_TRUE(LogUnreachableMemory(true, 100));
}
+TEST_F(MemunreachableTest, version) {
+ UnreachableMemoryInfo info;
+ info.version = 1;
+
+ ASSERT_FALSE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+}
+
} // namespace android
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 2d327ee..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -34,15 +34,6 @@
export_shared_lib_headers: ["libstatssocket"],
}
-// static version of libmetricslogger, needed by a few art static binaries
-// TODO(b/117829226): Remove once dependencies are cleaned up.
-cc_library_static {
- name: "libmetricslogger_static",
- srcs: metricslogger_lib_src_files,
- defaults: ["metricslogger_defaults"],
- export_shared_lib_headers: ["libstatssocket"],
-}
-
// metricslogger shared library, debug
// -----------------------------------------------------------------------------
cc_library_shared {
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 56bd6c4..71c04a6 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -116,7 +116,7 @@
FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
FIELD_BATTERY_CURRENT_UA = 1449,
FIELD_HARDWARE_LOCATION = 1450,
- ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
+ ACTION_BATTERY_CAUSED_SHUTDOWN = 1451,
};
enum {
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
new file mode 100644
index 0000000..a2824d1
--- /dev/null
+++ b/libmodprobe/Android.bp
@@ -0,0 +1,30 @@
+cc_library_static {
+ name: "libmodprobe",
+ cflags: [
+ "-Werror",
+ ],
+ recovery_available: true,
+ srcs: [
+ "libmodprobe.cpp",
+ "libmodprobe_ext.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ export_include_dirs: ["include/"],
+}
+
+cc_test {
+ name: "libmodprobe_tests",
+ cflags: ["-Werror"],
+ shared_libs: [
+ "libbase",
+ ],
+ local_include_dirs: ["include/"],
+ srcs: [
+ "libmodprobe_test.cpp",
+ "libmodprobe.cpp",
+ "libmodprobe_ext_test.cpp",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
new file mode 100644
index 0000000..0ec766a
--- /dev/null
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class Modprobe {
+ public:
+ Modprobe(const std::vector<std::string>&);
+
+ bool LoadListedModules();
+ bool LoadWithAliases(const std::string& module_name, bool strict);
+
+ private:
+ std::string MakeCanonical(const std::string& module_path);
+ bool InsmodWithDeps(const std::string& module_name);
+ bool Insmod(const std::string& path_name);
+ std::vector<std::string> GetDependencies(const std::string& module);
+ bool ModuleExists(const std::string& module_name);
+
+ bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
+ bool ParseAliasCallback(const std::vector<std::string>& args);
+ bool ParseSoftdepCallback(const std::vector<std::string>& args);
+ bool ParseLoadCallback(const std::vector<std::string>& args);
+ bool ParseOptionsCallback(const std::vector<std::string>& args);
+ void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
+
+ std::vector<std::pair<std::string, std::string>> module_aliases_;
+ std::unordered_map<std::string, std::vector<std::string>> module_deps_;
+ std::vector<std::pair<std::string, std::string>> module_pre_softdep_;
+ std::vector<std::pair<std::string, std::string>> module_post_softdep_;
+ std::vector<std::string> module_load_;
+ std::unordered_map<std::string, std::string> module_options_;
+};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
new file mode 100644
index 0000000..01cf2e3
--- /dev/null
+++ b/libmodprobe/libmodprobe.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <modprobe/modprobe.h>
+
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+std::string Modprobe::MakeCanonical(const std::string& module_path) {
+ auto start = module_path.find_last_of('/');
+ if (start == std::string::npos) {
+ start = 0;
+ } else {
+ start += 1;
+ }
+ auto end = module_path.size();
+ if (android::base::EndsWith(module_path, ".ko")) {
+ end -= 3;
+ }
+ if ((end - start) <= 1) {
+ LOG(ERROR) << "malformed module name: " << module_path;
+ return "";
+ }
+ std::string module_name = module_path.substr(start, end - start);
+ // module names can have '-', but their file names will have '_'
+ std::replace(module_name.begin(), module_name.end(), '-', '_');
+ return module_name;
+}
+
+bool Modprobe::ParseDepCallback(const std::string& base_path,
+ const std::vector<std::string>& args) {
+ std::vector<std::string> deps;
+ std::string prefix = "";
+
+ // Set first item as our modules path
+ std::string::size_type pos = args[0].find(':');
+ if (args[0][0] != '/') {
+ prefix = base_path + "/";
+ }
+ if (pos != std::string::npos) {
+ deps.emplace_back(prefix + args[0].substr(0, pos));
+ } else {
+ LOG(ERROR) << "dependency lines must start with name followed by ':'";
+ }
+
+ // Remaining items are dependencies of our module
+ for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
+ if ((*arg)[0] != '/') {
+ prefix = base_path + "/";
+ } else {
+ prefix = "";
+ }
+ deps.push_back(prefix + *arg);
+ }
+
+ std::string canonical_name = MakeCanonical(args[0].substr(0, pos));
+ if (canonical_name.empty()) {
+ return false;
+ }
+ this->module_deps_[canonical_name] = deps;
+
+ return true;
+}
+
+bool Modprobe::ParseAliasCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+
+ if (type != "alias") {
+ LOG(ERROR) << "non-alias line encountered in modules.alias, found " << type;
+ return false;
+ }
+
+ if (args.size() != 3) {
+ LOG(ERROR) << "alias lines in modules.alias must have 3 entries, not " << args.size();
+ return false;
+ }
+
+ const std::string& alias = *it++;
+ const std::string& module_name = *it++;
+ this->module_aliases_.emplace_back(alias, module_name);
+
+ return true;
+}
+
+bool Modprobe::ParseSoftdepCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+ std::string state = "";
+
+ if (type != "softdep") {
+ LOG(ERROR) << "non-softdep line encountered in modules.softdep, found " << type;
+ return false;
+ }
+
+ if (args.size() < 4) {
+ LOG(ERROR) << "softdep lines in modules.softdep must have at least 4 entries";
+ return false;
+ }
+
+ const std::string& module = *it++;
+ while (it != args.end()) {
+ const std::string& token = *it++;
+ if (token == "pre:" || token == "post:") {
+ state = token;
+ continue;
+ }
+ if (state == "") {
+ LOG(ERROR) << "malformed modules.softdep at token " << token;
+ return false;
+ }
+ if (state == "pre:") {
+ this->module_pre_softdep_.emplace_back(module, token);
+ } else {
+ this->module_post_softdep_.emplace_back(module, token);
+ }
+ }
+
+ return true;
+}
+
+bool Modprobe::ParseLoadCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& module = *it++;
+
+ const std::string& canonical_name = MakeCanonical(module);
+ if (canonical_name.empty()) {
+ return false;
+ }
+ this->module_load_.emplace_back(canonical_name);
+
+ return true;
+}
+
+bool Modprobe::ParseOptionsCallback(const std::vector<std::string>& args) {
+ auto it = args.begin();
+ const std::string& type = *it++;
+
+ if (type != "options") {
+ LOG(ERROR) << "non-options line encountered in modules.options";
+ return false;
+ }
+
+ if (args.size() < 2) {
+ LOG(ERROR) << "lines in modules.options must have at least 2 entries, not " << args.size();
+ return false;
+ }
+
+ const std::string& module = *it++;
+ std::string options = "";
+
+ const std::string& canonical_name = MakeCanonical(module);
+ if (canonical_name.empty()) {
+ return false;
+ }
+
+ while (it != args.end()) {
+ options += *it++;
+ if (it != args.end()) {
+ options += " ";
+ }
+ }
+
+ auto [unused, inserted] = this->module_options_.emplace(canonical_name, options);
+ if (!inserted) {
+ LOG(ERROR) << "multiple options lines present for module " << module;
+ return false;
+ }
+ return true;
+}
+
+void Modprobe::ParseCfg(const std::string& cfg,
+ std::function<bool(const std::vector<std::string>&)> f) {
+ std::string cfg_contents;
+ if (!android::base::ReadFileToString(cfg, &cfg_contents, false)) {
+ return;
+ }
+
+ std::vector<std::string> lines = android::base::Split(cfg_contents, "\n");
+ for (const std::string line : lines) {
+ if (line.empty() || line[0] == '#') {
+ continue;
+ }
+ const std::vector<std::string> args = android::base::Split(line, " ");
+ if (args.empty()) continue;
+ f(args);
+ }
+ return;
+}
+
+Modprobe::Modprobe(const std::vector<std::string>& base_paths) {
+ using namespace std::placeholders;
+
+ for (const auto& base_path : base_paths) {
+ auto alias_callback = std::bind(&Modprobe::ParseAliasCallback, this, _1);
+ ParseCfg(base_path + "/modules.alias", alias_callback);
+
+ auto dep_callback = std::bind(&Modprobe::ParseDepCallback, this, base_path, _1);
+ ParseCfg(base_path + "/modules.dep", dep_callback);
+
+ auto softdep_callback = std::bind(&Modprobe::ParseSoftdepCallback, this, _1);
+ ParseCfg(base_path + "/modules.softdep", softdep_callback);
+
+ auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);
+ ParseCfg(base_path + "/modules.load", load_callback);
+
+ auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
+ ParseCfg(base_path + "/modules.options", options_callback);
+ }
+}
+
+std::vector<std::string> Modprobe::GetDependencies(const std::string& module) {
+ auto it = module_deps_.find(module);
+ if (it == module_deps_.end()) {
+ return {};
+ }
+ return it->second;
+}
+
+bool Modprobe::InsmodWithDeps(const std::string& module_name) {
+ if (module_name.empty()) {
+ LOG(ERROR) << "Need valid module name, given: " << module_name;
+ return false;
+ }
+
+ auto dependencies = GetDependencies(module_name);
+ if (dependencies.empty()) {
+ LOG(ERROR) << "Module " << module_name << " not in dependency file";
+ return false;
+ }
+
+ // load module dependencies in reverse order
+ for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
+ const std::string& canonical_name = MakeCanonical(*dep);
+ if (canonical_name.empty()) {
+ return false;
+ }
+ if (!LoadWithAliases(canonical_name, true)) {
+ return false;
+ }
+ }
+
+ // try to load soft pre-dependencies
+ for (const auto& [module, softdep] : module_pre_softdep_) {
+ if (module_name == module) {
+ LoadWithAliases(softdep, false);
+ }
+ }
+
+ // load target module itself with args
+ if (!Insmod(dependencies[0])) {
+ return false;
+ }
+
+ // try to load soft post-dependencies
+ for (const auto& [module, softdep] : module_post_softdep_) {
+ if (module_name == module) {
+ LoadWithAliases(softdep, false);
+ }
+ }
+
+ return true;
+}
+
+bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) {
+ std::set<std::string> modules_to_load = {module_name};
+ bool module_loaded = false;
+
+ // use aliases to expand list of modules to load (multiple modules
+ // may alias themselves to the requested name)
+ for (const auto& [alias, aliased_module] : module_aliases_) {
+ if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
+ modules_to_load.emplace(aliased_module);
+ }
+
+ // attempt to load all modules aliased to this name
+ for (const auto& module : modules_to_load) {
+ if (!ModuleExists(module)) continue;
+ if (InsmodWithDeps(module)) module_loaded = true;
+ }
+
+ if (strict && !module_loaded) {
+ LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name;
+ return false;
+ }
+ return true;
+}
+
+bool Modprobe::LoadListedModules() {
+ for (const auto& module : module_load_) {
+ if (!LoadWithAliases(module, true)) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
new file mode 100644
index 0000000..5f3a04d
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <modprobe/modprobe.h>
+
+bool Modprobe::Insmod(const std::string& path_name) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ if (fd == -1) {
+ LOG(ERROR) << "Could not open module '" << path_name << "'";
+ return false;
+ }
+
+ std::string options = "";
+ auto options_iter = module_options_.find(MakeCanonical(path_name));
+ if (options_iter != module_options_.end()) {
+ options = options_iter->second;
+ }
+
+ LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
+ int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
+ if (ret != 0) {
+ if (errno == EEXIST) {
+ // Module already loaded
+ return true;
+ }
+ LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
+ return false;
+ }
+
+ LOG(INFO) << "Loaded kernel module " << path_name;
+ return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+ struct stat fileStat;
+ auto deps = GetDependencies(module_name);
+ if (deps.empty()) {
+ // missing deps can happen in the case of an alias
+ return false;
+ }
+ if (stat(deps.front().c_str(), &fileStat)) {
+ return false;
+ }
+ if (!S_ISREG(fileStat.st_mode)) {
+ return false;
+ }
+ return true;
+}
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
new file mode 100644
index 0000000..0f073cb
--- /dev/null
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+bool Modprobe::Insmod(const std::string& path_name) {
+ auto deps = GetDependencies(MakeCanonical(path_name));
+ if (deps.empty()) {
+ return false;
+ }
+ if (std::find(test_modules.begin(), test_modules.end(), deps.front()) == test_modules.end()) {
+ return false;
+ }
+ for (auto it = modules_loaded.begin(); it != modules_loaded.end(); ++it) {
+ if (android::base::StartsWith(*it, path_name)) {
+ return true;
+ }
+ }
+ std::string options;
+ auto options_iter = module_options_.find(MakeCanonical(path_name));
+ if (options_iter != module_options_.end()) {
+ options = " " + options_iter->second;
+ }
+ modules_loaded.emplace_back(path_name + options);
+ return true;
+}
+
+bool Modprobe::ModuleExists(const std::string& module_name) {
+ auto deps = GetDependencies(module_name);
+ if (deps.empty()) {
+ // missing deps can happen in the case of an alias
+ return false;
+ }
+ return std::find(test_modules.begin(), test_modules.end(), deps.front()) != test_modules.end();
+}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
new file mode 100644
index 0000000..481658d
--- /dev/null
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+
+#include <modprobe/modprobe.h>
+
+#include "libmodprobe_test.h"
+
+// Used by libmodprobe_ext_test to check if requested modules are present.
+std::vector<std::string> test_modules;
+
+// Used by libmodprobe_ext_test to report which modules would have been loaded.
+std::vector<std::string> modules_loaded;
+
+TEST(libmodprobe, Test) {
+ test_modules = {
+ "/test1.ko", "/test2.ko", "/test3.ko", "/test4.ko", "/test5.ko",
+ "/test6.ko", "/test7.ko", "/test8.ko", "/test9.ko", "/test10.ko",
+ "/test11.ko", "/test12.ko", "/test13.ko", "/test14.ko", "/test15.ko",
+ };
+
+ std::vector<std::string> expected_modules_loaded = {
+ "/test14.ko",
+ "/test15.ko",
+ "/test3.ko",
+ "/test4.ko",
+ "/test1.ko",
+ "/test6.ko",
+ "/test2.ko",
+ "/test5.ko",
+ "/test8.ko",
+ "/test7.ko param1=4",
+ "/test9.ko param_x=1 param_y=2 param_z=3",
+ "/test10.ko",
+ "/test12.ko",
+ "/test11.ko",
+ "/test13.ko",
+ };
+
+ const std::string modules_dep =
+ "test1.ko:\n"
+ "test2.ko:\n"
+ "test3.ko:\n"
+ "test4.ko: test3.ko\n"
+ "test5.ko: test2.ko test6.ko\n"
+ "test6.ko:\n"
+ "test7.ko:\n"
+ "test8.ko:\n"
+ "test9.ko:\n"
+ "test10.ko:\n"
+ "test11.ko:\n"
+ "test12.ko:\n"
+ "test13.ko:\n"
+ "test14.ko:\n"
+ "test15.ko:\n";
+
+ const std::string modules_softdep =
+ "softdep test7 pre: test8\n"
+ "softdep test9 post: test10\n"
+ "softdep test11 pre: test12 post: test13\n"
+ "softdep test3 pre: test141516\n";
+
+ const std::string modules_alias =
+ "# Aliases extracted from modules themselves.\n"
+ "\n"
+ "alias test141516 test14\n"
+ "alias test141516 test15\n"
+ "alias test141516 test16\n";
+
+ const std::string modules_options =
+ "options test7.ko param1=4\n"
+ "options test9.ko param_x=1 param_y=2 param_z=3\n"
+ "options test100.ko param_1=1\n";
+
+ const std::string modules_load =
+ "test4.ko\n"
+ "test1.ko\n"
+ "test3.ko\n"
+ "test5.ko\n"
+ "test7.ko\n"
+ "test9.ko\n"
+ "test11.ko\n";
+
+ TemporaryDir dir;
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid()));
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid()));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid()));
+
+ for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
+ *i = dir.path + *i;
+ }
+
+ Modprobe m({dir.path});
+ EXPECT_TRUE(m.LoadListedModules());
+
+ GTEST_LOG_(INFO) << "Expected modules loaded (in order):";
+ for (auto i = expected_modules_loaded.begin(); i != expected_modules_loaded.end(); ++i) {
+ *i = dir.path + *i;
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+ GTEST_LOG_(INFO) << "Actual modules loaded (in order):";
+ for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+ GTEST_LOG_(INFO) << "\"" << *i << "\"";
+ }
+
+ EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libmodprobe/libmodprobe_test.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to libmodprobe/libmodprobe_test.h
index 410d379..a001b69 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libmodprobe/libmodprobe_test.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include <string>
+#include <vector>
+
+extern std::vector<std::string> test_modules;
+extern std::vector<std::string> modules_loaded;
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 92fd2a2..10d42e4 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,5 +1,18 @@
+cc_defaults {
+ name: "libnativebridge-defaults",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ cppflags: [
+ "-fvisibility=protected",
+ ],
+ header_libs: ["libnativebridge-headers"],
+ export_header_lib_headers: ["libnativebridge-headers"],
+}
+
cc_library_headers {
- name: "libnativebridge-dummy-headers",
+ name: "libnativebridge-headers",
host_supported: true,
export_include_dirs: ["include"],
@@ -7,6 +20,7 @@
cc_library {
name: "libnativebridge",
+ defaults: ["libnativebridge-defaults"],
host_supported: true,
srcs: ["native_bridge.cc"],
@@ -16,16 +30,32 @@
shared_libs: [
"liblog",
],
-
+ // TODO(jiyong): remove this line after aosp/885921 lands
export_include_dirs: ["include"],
- cflags: [
- "-Werror",
- "-Wall",
- ],
- cppflags: [
- "-fvisibility=protected",
- ],
+ target: {
+ android: {
+ version_script: "libnativebridge.map.txt",
+ },
+ linux: {
+ version_script: "libnativebridge.map.txt",
+ },
+ },
+
+ stubs: {
+ symbol_file: "libnativebridge.map.txt",
+ versions: ["1"],
+ },
+}
+
+// TODO(b/124250621): eliminate the need for this library
+cc_library {
+ name: "libnativebridge_lazy",
+ defaults: ["libnativebridge-defaults"],
+
+ host_supported: false,
+ srcs: ["native_bridge_lazy.cc"],
+ required: ["libnativebridge"],
}
subdirs = ["tests"]
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk
deleted file mode 100644
index 3887b1b..0000000
--- a/libnativebridge/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index f2cc942..daf87f4 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1 +1,4 @@
dimitry@google.com
+eaeltsin@google.com
+ngeoffray@google.com
+oth@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 28f1927..e9c9500 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -17,12 +17,17 @@
#ifndef NATIVE_BRIDGE_H_
#define NATIVE_BRIDGE_H_
-#include "jni.h"
#include <signal.h>
+#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
+#include "jni.h"
+
+#ifdef __cplusplus
namespace android {
+extern "C" {
+#endif // __cplusplus
struct NativeBridgeRuntimeCallbacks;
struct NativeBridgeRuntimeValues;
@@ -32,11 +37,10 @@
// to the chain.
typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
-
// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
// signals that we do not want to load a native bridge.
bool LoadNativeBridge(const char* native_bridge_library_filename,
- const NativeBridgeRuntimeCallbacks* runtime_callbacks);
+ const struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
// Quick check whether a native bridge will be needed. This is based off of the instruction set
// of the process.
@@ -138,19 +142,17 @@
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
-native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- native_bridge_namespace_t* parent_ns);
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns);
// Creates a link which shares some libraries from one namespace to another.
// NativeBridge's peer of android_link_namespaces() of dynamic linker.
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
-bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to,
const char* shared_libs_sonames);
// Load a shared library with namespace key that is supported by the native bridge.
@@ -159,10 +161,12 @@
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns);
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns);
-// Returns vendor namespace if it is enabled for the device and null otherwise
-native_bridge_namespace_t* NativeBridgeGetVendorNamespace();
+// Returns exported namespace by the name. This is a reflection of
+// android_get_exported_namespace function. Introduced in v5.
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name);
// Native bridge interfaces to runtime.
struct NativeBridgeCallbacks {
@@ -177,8 +181,8 @@
// runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
// Returns:
// true if initialization was successful.
- bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir,
- const char* instruction_set);
+ bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs,
+ const char* private_dir, const char* instruction_set);
// Load a shared library that is supported by the native bridge.
//
@@ -314,12 +318,12 @@
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
- native_bridge_namespace_t* (*createNamespace)(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- native_bridge_namespace_t* parent_ns);
+ struct native_bridge_namespace_t* (*createNamespace)(const char* name,
+ const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ struct native_bridge_namespace_t* parent_ns);
// Creates a link which shares some libraries from one namespace to another.
// NativeBridge's peer of android_link_namespaces() of dynamic linker.
@@ -334,8 +338,8 @@
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Should not use in non-namespace scenario.
- bool (*linkNamespaces)(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
- const char* shared_libs_sonames);
+ bool (*linkNamespaces)(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to, const char* shared_libs_sonames);
// Load a shared library within a namespace.
// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
@@ -350,7 +354,7 @@
//
// Starting with v3, NativeBridge has two scenarios: with/without namespace.
// Use loadLibrary instead in non-namespace scenario.
- void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns);
+ void* (*loadLibraryExt)(const char* libpath, int flag, struct native_bridge_namespace_t* ns);
// Get native bridge version of vendor namespace.
// The vendor namespace is the namespace used to load vendor public libraries.
@@ -359,7 +363,17 @@
//
// Returns:
// vendor namespace or null if it was not set up for the device
- native_bridge_namespace_t* (*getVendorNamespace)();
+ //
+ // Starting with v5 (Android Q) this function is no longer used.
+ // Use getExportedNamespace() below.
+ struct native_bridge_namespace_t* (*getVendorNamespace)();
+
+ // Get native bridge version of exported namespace. Peer of
+ // android_get_exported_namespace(const char*) function.
+ //
+ // Returns:
+ // exported namespace or null if it was not set up for the device
+ struct native_bridge_namespace_t* (*getExportedNamespace)(const char* name);
};
// Runtime interfaces to native bridge.
@@ -396,6 +410,9 @@
uint32_t method_count);
};
-}; // namespace android
+#ifdef __cplusplus
+} // extern "C"
+} // namespace android
+#endif // __cplusplus
#endif // NATIVE_BRIDGE_H_
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
new file mode 100644
index 0000000..a6841a3
--- /dev/null
+++ b/libnativebridge/libnativebridge.map.txt
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# TODO(b/122710865): Most of these uses come from libnativeloader, which should be bundled
+# together with libnativebridge in the APEX. Once this happens, prune this list.
+LIBNATIVEBRIDGE_1 {
+ global:
+ NativeBridgeIsSupported;
+ NativeBridgeLoadLibrary;
+ NativeBridgeUnloadLibrary;
+ NativeBridgeGetError;
+ NativeBridgeIsPathSupported;
+ NativeBridgeCreateNamespace;
+ NativeBridgeGetExportedNamespace;
+ NativeBridgeLinkNamespaces;
+ NativeBridgeLoadLibraryExt;
+ NativeBridgeInitAnonymousNamespace;
+ NativeBridgeInitialized;
+ NativeBridgeGetTrampoline;
+ LoadNativeBridge;
+ PreInitializeNativeBridge;
+ InitializeNativeBridge;
+ NativeBridgeGetVersion;
+ NativeBridgeGetSignalHandler;
+ UnloadNativeBridge;
+ NativeBridgeAvailable;
+ NeedsNativeBridge;
+ NativeBridgeError;
+ NativeBridgeNameAcceptable;
+ local:
+ *;
+};
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index e24307a..9adba9a 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -33,6 +33,13 @@
namespace android {
+#ifdef __APPLE__
+template <typename T>
+void UNUSED(const T&) {}
+#endif
+
+extern "C" {
+
// Environment values required by the apps running with native bridge.
struct NativeBridgeRuntimeValues {
const char* os_arch;
@@ -94,6 +101,8 @@
NAMESPACE_VERSION = 3,
// The version with vendor namespaces
VENDOR_NAMESPACE_VERSION = 4,
+ // The version with runtime namespaces
+ RUNTIME_NAMESPACE_VERSION = 5,
};
// Whether we had an error at some point.
@@ -252,10 +261,6 @@
return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
}
-#ifdef __APPLE__
-template<typename T> void UNUSED(const T&) {}
-#endif
-
bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) {
if (state != NativeBridgeState::kOpened) {
ALOGE("Invalid state: native bridge is expected to be opened.");
@@ -607,12 +612,22 @@
return false;
}
-native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
- if (!NativeBridgeInitialized() || !isCompatibleWith(VENDOR_NAMESPACE_VERSION)) {
+native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+ if (!NativeBridgeInitialized()) {
return nullptr;
}
- return callbacks->getVendorNamespace();
+ if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) {
+ return callbacks->getExportedNamespace(name);
+ }
+
+ // sphal is vendor namespace name -> use v4 callback in the case NB callbacks
+ // are not compatible with v5
+ if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) {
+ return callbacks->getVendorNamespace();
+ }
+
+ return nullptr;
}
void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) {
@@ -626,4 +641,6 @@
return nullptr;
}
-}; // namespace android
+} // extern "C"
+
+} // namespace android
diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc
new file mode 100644
index 0000000..94c8084
--- /dev/null
+++ b/libnativebridge/native_bridge_lazy.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nativebridge/native_bridge.h"
+#define LOG_TAG "nativebridge"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativebridge.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+bool LoadNativeBridge(const char* native_bridge_library_filename,
+ const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) {
+ static auto f = GET_FUNC_PTR(LoadNativeBridge);
+ return f(native_bridge_library_filename, runtime_callbacks);
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(NeedsNativeBridge);
+ return f(instruction_set);
+}
+
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(PreInitializeNativeBridge);
+ return f(app_data_dir, instruction_set);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+ static auto f = GET_FUNC_PTR(InitializeNativeBridge);
+ return f(env, instruction_set);
+}
+
+void UnloadNativeBridge() {
+ static auto f = GET_FUNC_PTR(UnloadNativeBridge);
+ return f();
+}
+
+bool NativeBridgeAvailable() {
+ static auto f = GET_FUNC_PTR(NativeBridgeAvailable);
+ return f();
+}
+
+bool NativeBridgeInitialized() {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitialized);
+ return f();
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary);
+ return f(libpath, flag);
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline);
+ return f(handle, name, shorty, len);
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsSupported);
+ return f(libpath);
+}
+
+uint32_t NativeBridgeGetVersion() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVersion);
+ return f();
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler);
+ return f(signal);
+}
+
+bool NativeBridgeError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeError);
+ return f();
+}
+
+bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) {
+ static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable);
+ return f(native_bridge_library_filename);
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+ static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary);
+ return f(handle);
+}
+
+const char* NativeBridgeGetError() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetError);
+ return f();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported);
+ return f(path);
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace);
+ return f(public_ns_sonames, anon_ns_library_path);
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent_ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace);
+ return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path,
+ parent_ns);
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to,
+ const char* shared_libs_sonames) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces);
+ return f(from, to, shared_libs_sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt);
+ return f(libpath, flag, ns);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() {
+ static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 9e2e641..2bb8467 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,7 +23,7 @@
"-Wextra",
"-Werror",
],
- header_libs: ["libnativebridge-dummy-headers"],
+ header_libs: ["libnativebridge-headers"],
cppflags: ["-fvisibility=protected"],
}
@@ -44,3 +44,67 @@
srcs: ["DummyNativeBridge3.cpp"],
defaults: ["libnativebridge-dummy-defaults"],
}
+
+// Build the unit tests.
+cc_defaults {
+ name: "libnativebridge-tests-defaults",
+ test_per_src: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ srcs: [
+ "CodeCacheCreate_test.cpp",
+ "CodeCacheExists_test.cpp",
+ "CodeCacheStatFail_test.cpp",
+ "CompleteFlow_test.cpp",
+ "InvalidCharsNativeBridge_test.cpp",
+ "NativeBridge2Signal_test.cpp",
+ "NativeBridgeVersion_test.cpp",
+ "NeedsNativeBridge_test.cpp",
+ "PreInitializeNativeBridge_test.cpp",
+ "PreInitializeNativeBridgeFail1_test.cpp",
+ "PreInitializeNativeBridgeFail2_test.cpp",
+ "ReSetupNativeBridge_test.cpp",
+ "UnavailableNativeBridge_test.cpp",
+ "ValidNameNativeBridge_test.cpp",
+ "NativeBridge3UnloadLibrary_test.cpp",
+ "NativeBridge3GetError_test.cpp",
+ "NativeBridge3IsPathSupported_test.cpp",
+ "NativeBridge3InitAnonymousNamespace_test.cpp",
+ "NativeBridge3CreateNamespace_test.cpp",
+ "NativeBridge3LoadLibraryExt_test.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libnativebridge-dummy",
+ ],
+ header_libs: ["libbase_headers"],
+}
+
+cc_test {
+ name: "libnativebridge-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ host_supported: true,
+ shared_libs: ["libnativebridge"],
+}
+
+cc_test {
+ name: "libnativebridge-lazy-tests",
+ defaults: ["libnativebridge-tests-defaults"],
+ shared_libs: ["libnativebridge_lazy"],
+}
+
+// Build the test for the C API.
+cc_test {
+ name: "libnativebridge-api-tests",
+ host_supported: true,
+ test_per_src: true,
+ srcs: [
+ "NativeBridgeApi.c",
+ ],
+ header_libs: ["libnativebridge-headers"],
+}
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
deleted file mode 100644
index 4ed6e20..0000000
--- a/libnativebridge/tests/Android.mk
+++ /dev/null
@@ -1,60 +0,0 @@
-# Build the unit tests.
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the unit tests.
-test_src_files := \
- CodeCacheCreate_test.cpp \
- CodeCacheExists_test.cpp \
- CodeCacheStatFail_test.cpp \
- CompleteFlow_test.cpp \
- InvalidCharsNativeBridge_test.cpp \
- NativeBridge2Signal_test.cpp \
- NativeBridgeVersion_test.cpp \
- NeedsNativeBridge_test.cpp \
- PreInitializeNativeBridge_test.cpp \
- PreInitializeNativeBridgeFail1_test.cpp \
- PreInitializeNativeBridgeFail2_test.cpp \
- ReSetupNativeBridge_test.cpp \
- UnavailableNativeBridge_test.cpp \
- ValidNameNativeBridge_test.cpp \
- NativeBridge3UnloadLibrary_test.cpp \
- NativeBridge3GetError_test.cpp \
- NativeBridge3IsPathSupported_test.cpp \
- NativeBridge3InitAnonymousNamespace_test.cpp \
- NativeBridge3CreateNamespace_test.cpp \
- NativeBridge3LoadLibraryExt_test.cpp
-
-
-shared_libraries := \
- liblog \
- libnativebridge \
- libnativebridge-dummy
-
-header_libraries := \
- libbase_headers
-
-libnativebridge_tests_common_cflags := \
- -Wall \
- -Werror \
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_HEADER_LIBRARIES := $(header_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_CFLAGS := $(libnativebridge_tests_common_cflags)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_HOST_NATIVE_TEST)) \
-)
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/libnativebridge/tests/NativeBridgeApi.c
similarity index 64%
copy from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
copy to libnativebridge/tests/NativeBridgeApi.c
index 5d0d924..7ab71fe 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/libnativebridge/tests/NativeBridgeApi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativebridge/native_bridge.h"
-#include <stdint.h>
-#include <string>
-
-namespace unwindstack {
-class Memory;
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ return 0;
}
-
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 17983bc..b860db9 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -1,16 +1,7 @@
// Shared library for target
// ========================================================
-cc_library {
- name: "libnativeloader",
- host_supported: true,
- srcs: ["native_loader.cpp"],
- shared_libs: [
- "libnativehelper",
- "liblog",
- "libcutils",
- "libnativebridge",
- "libbase",
- ],
+cc_defaults {
+ name: "libnativeloader-defaults",
cflags: [
"-Werror",
"-Wall",
@@ -18,9 +9,88 @@
cppflags: [
"-fvisibility=hidden",
],
- export_include_dirs: ["include"],
+ header_libs: ["libnativeloader-headers"],
+ export_header_lib_headers: ["libnativeloader-headers"],
+}
+
+cc_library {
+ name: "libnativeloader",
+ defaults: ["libnativeloader-defaults"],
+ host_supported: true,
+ srcs: [
+ "native_loader.cpp",
+ ],
+ shared_libs: [
+ "libnativehelper",
+ "liblog",
+ "libnativebridge",
+ "libbase",
+ ],
+ target: {
+ android: {
+ srcs: [
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
+ shared_libs: [
+ "libdl_android",
+ ],
+ },
+ },
required: [
"llndk.libraries.txt",
"vndksp.libraries.txt",
],
+ stubs: {
+ symbol_file: "libnativeloader.map.txt",
+ versions: ["1"],
+ },
+}
+
+// TODO(b/124250621) eliminate the need for this library
+cc_library {
+ name: "libnativeloader_lazy",
+ defaults: ["libnativeloader-defaults"],
+ host_supported: false,
+ srcs: ["native_loader_lazy.cpp"],
+ required: ["libnativeloader"],
+}
+
+cc_library_headers {
+ name: "libnativeloader-headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+// TODO(jiyong) Remove this when its use in the internal master is
+// switched to libnativeloader-headers
+cc_library_headers {
+ name: "libnativeloader-dummy-headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "libnativeloader_test",
+ srcs: [
+ "native_loader_test.cpp",
+ "native_loader.cpp",
+ "library_namespaces.cpp",
+ "native_loader_namespace.cpp",
+ "public_libraries.cpp",
+ ],
+ cflags: ["-DANDROID"],
+ static_libs: [
+ "libbase",
+ "liblog",
+ "libnativehelper",
+ "libgmock",
+ ],
+ header_libs: [
+ "libnativebridge-headers",
+ "libnativeloader-headers",
+ ],
+ system_shared_libs: ["libc", "libm"],
+ test_suites: ["device-tests"],
}
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index 5c28a9f..f735653 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1,2 +1,6 @@
dimitry@google.com
jiyong@google.com
+ngeoffray@google.com
+oth@google.com
+mast@google.com
+rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
new file mode 100644
index 0000000..46f6fdd
--- /dev/null
+++ b/libnativeloader/README.md
@@ -0,0 +1,84 @@
+libnativeloader
+===============================================================================
+
+Overview
+-------------------------------------------------------------------------------
+libnativeloader is responsible for loading native shared libraries (`*.so`
+files) inside the Android Runtime (ART). The native shared libraries could be
+app-provided JNI libraries or public native libraries like `libc.so` provided
+by the platform.
+
+The most typical use case of this library is calling `System.loadLibrary(name)`.
+When the method is called, the ART runtime delegates the call to this library
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (named `classloader-namespace`) that is
+associated with the given classloader, and tries to load the requested library
+from the namespace. The actual searching, loading, and linking of the library
+is performed by the dynamic linker.
+
+The linker namespace is created when an APK is loaded into the process, and is
+associated with the classloader that loaded the APK. The linker namespace is
+configured so that only the JNI libraries embedded in the APK is accessible
+from the namespace, thus preventing an APK from loading JNI libraries of other
+APKs.
+
+The linker namespace is also configured differently depending on other
+characteristics of the APK such as whether or not the APK is bundled with the
+platform. In case of the unbundled, i.e., downloaded or updated APK, only the
+public native libraries that is listed in `/system/etc/public.libraries.txt`
+are available from the platform, whereas in case of the bundled, all libraries
+under `/system/lib` are available (i.e. shared). In case when the unbundled
+app is from `/vendor` or `/product` partition, the app is additionally provided
+with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
+libraries. As the platform is getting modularized with
+[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
+some libraries are no longer provided from platform, but from the APEXes which
+have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
+`libicui18n.so` are from the runtime APEX.
+
+The list of public native libraries is not static. The default set of libraries
+are defined in AOSP, but partners can extend it to include their own libraries.
+Currently, following extensions are available:
+
+- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
+specific to the underlying SoC, e.g. GPU, DSP, etc.
+- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
+`/{system|system}/lib` that a device manufacturer has newly added. The
+libraries should be named as `lib<name>.<companyname>.so` as in
+`libFoo.acme.so`.
+
+Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
+is prohibited for a device manufacturer to expose an AOSP-defined private
+library, e.g. libgui.so, libart.so, etc., to APKs.
+
+Lastly, libnativeloader is responsible for abstracting the two types of the
+dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
+for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
+loading libraries in a translated environment such as ARM-on-x86.
+
+Implementation
+-------------------------------------------------------------------------------
+Implementation wise, libnativeloader consists of four parts:
+
+- `native_loader.cpp`
+- `library_namespaces.cpp`
+- `native_loader_namespace.cpp`
+- `public_libraries.cpp`
+
+`native_loader.cpp` implements the public interface of this library. It is just
+a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
+
+`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
+is a manager-like entity that is responsible for creating and configuring
+linker namespaces and finding an already created linker namespace for a given
+classloader.
+
+`native_loader_namesapces.cpp` implements the class `NativeLoaderNamespace` that
+models a linker namespace. It's main job is to abstract the two types of the
+dynamic linker interface so that other parts of this library do not have to know
+the differences of the interfaces.
+
+`public_libraries.cpp` is responsible for reading `*.txt` files for the public
+native libraries from the various partitions. It can be considered as a part of
+`LibraryNamespaces` but is separated from it to hide the details of the parsing
+routines.
diff --git a/libnativeloader/TEST_MAPPING b/libnativeloader/TEST_MAPPING
new file mode 100644
index 0000000..7becb77
--- /dev/null
+++ b/libnativeloader/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "libnativeloader_test"
+ }
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/tests/jni"
+ }
+ ]
+}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
index 43c9329..2d6ce85 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -18,6 +18,7 @@
#define __ANDROID_DLEXT_NAMESPACES_H__
#include <android/dlext.h>
+#include <stdbool.h>
__BEGIN_DECLS
@@ -84,12 +85,9 @@
* If a library or any of its dependencies are outside of the permitted_when_isolated_path
* and search_path, and it is not part of the public namespace dlopen will fail.
*/
-extern struct android_namespace_t* android_create_namespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- android_namespace_t* parent);
+extern struct android_namespace_t* android_create_namespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct android_namespace_t* parent);
/*
* Creates a link between namespaces. Every link has list of sonames of
@@ -107,24 +105,11 @@
* step will not go deeper into linked namespaces for this library but
* will do so for DT_NEEDED libraries.
*/
-extern bool android_link_namespaces(android_namespace_t* from,
- android_namespace_t* to,
+extern bool android_link_namespaces(struct android_namespace_t* from,
+ struct android_namespace_t* to,
const char* shared_libs_sonames);
-/*
- * Get the default library search path.
- * The path will be copied into buffer, which must have space for at least
- * buffer_size chars. Elements are separated with ':', and the path will always
- * be null-terminated.
- *
- * If buffer_size is too small to hold the entire default search path and the
- * null terminator, this function will abort. There is currently no way to find
- * out what the required buffer size is. At the time of this writing, PATH_MAX
- * is sufficient and used by all callers of this function.
- */
-extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
-
-extern android_namespace_t* android_get_exported_namespace(const char* name);
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
__END_DECLS
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index c1d9d2a..51fb875 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -17,63 +17,62 @@
#ifndef NATIVE_LOADER_H_
#define NATIVE_LOADER_H_
-#include "jni.h"
+#include <stdbool.h>
#include <stdint.h>
-#include <string>
+#include "jni.h"
#if defined(__ANDROID__)
#include <android/dlext.h>
#endif
+#ifdef __cplusplus
namespace android {
+extern "C" {
+#endif // __cplusplus
+
+// README: the char** error message parameter being passed
+// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage.
+// It's the caller's responsibility to call that method.
__attribute__((visibility("default")))
void InitializeNativeLoader();
-__attribute__((visibility("default")))
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
- jstring permitted_path);
+__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace(
+ JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path,
+ jstring library_path, jstring permitted_path);
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(JNIEnv* env,
- int32_t target_sdk_version,
- const char* path,
- jobject class_loader,
- jstring library_path,
- bool* needs_native_bridge,
- std::string* error_msg);
+__attribute__((visibility("default"))) void* OpenNativeLibrary(
+ JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader,
+ const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg);
__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
const bool needs_native_bridge,
- std::string* error_msg);
+ char** error_msg);
+
+__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg);
#if defined(__ANDROID__)
// Look up linker namespace by class_loader. Returns nullptr if
// there is no namespace associated with the class_loader.
// TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function.
-__attribute__((visibility("default")))
-android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
-class NativeLoaderNamespace;
-__attribute__((visibility("default")))
-NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(
+__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader(
JNIEnv* env, jobject class_loader);
+// That version works with native bridge namespaces, but requires use of OpenNativeLibrary.
+struct NativeLoaderNamespace;
+__attribute__((visibility("default"))) struct NativeLoaderNamespace*
+FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
// Load library. Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does
// not require access to JNIEnv either.
-__attribute__((visibility("default")))
-void* OpenNativeLibrary(NativeLoaderNamespace* ns,
- const char* path,
- bool* needs_native_bridge,
- std::string* error_msg);
+__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace(
+ struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
+ char** error_msg);
#endif
__attribute__((visibility("default")))
void ResetNativeLoader();
-}; // namespace android
+#ifdef __cplusplus
+} // extern "C"
+} // namespace android
+#endif // __cplusplus
#endif // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
new file mode 100644
index 0000000..40c30bd
--- /dev/null
+++ b/libnativeloader/libnativeloader.map.txt
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# TODO(b/122710865): Prune these uses once the runtime APEX is complete.
+LIBNATIVELOADER_1 {
+ global:
+ OpenNativeLibrary;
+ InitializeNativeLoader;
+ ResetNativeLoader;
+ CloseNativeLibrary;
+ OpenNativeLibraryInNamespace;
+ FindNamespaceByClassLoader;
+ FindNativeLoaderNamespaceByClassLoader;
+ CreateClassLoaderNamespace;
+ NativeLoaderFreeErrorMessage;
+ local:
+ *;
+};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
new file mode 100644
index 0000000..a9eea8c
--- /dev/null
+++ b/libnativeloader/library_namespaces.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "library_namespaces.h"
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "nativeloader/dlext_namespaces.h"
+#include "public_libraries.h"
+#include "utils.h"
+
+using android::base::Error;
+
+namespace android::nativeloader {
+
+namespace {
+// The device may be configured to have the vendor libraries loaded to a separate namespace.
+// For historical reasons this namespace was named sphal but effectively it is intended
+// to use to load vendor libraries to separate namespace with controlled interface between
+// vendor and system namespaces.
+constexpr const char* kVendorNamespaceName = "sphal";
+constexpr const char* kVndkNamespaceName = "vndk";
+constexpr const char* kRuntimeNamespaceName = "runtime";
+constexpr const char* kNeuralNetworksNamespaceName = "neuralnetworks";
+
+// classloader-namespace is a linker namespace that is created for the loaded
+// app. To be specific, it is created for the app classloader. When
+// System.load() is called from a Java class that is loaded from the
+// classloader, the classloader-namespace namespace associated with that
+// classloader is selected for dlopen. The namespace is configured so that its
+// search path is set to the app-local JNI directory and it is linked to the
+// platform namespace with the names of libs listed in the public.libraries.txt.
+// This way an app can only load its own JNI libraries along with the public libs.
+constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
+
+// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
+// System.load() with an absolute path which is outside of the classloader library search path.
+// This list includes all directories app is allowed to access this way.
+constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
+
+constexpr const char* kVendorLibPath = "/vendor/" LIB;
+constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
+
+const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
+
+// Define origin of APK if it is from vendor partition or product partition
+typedef enum {
+ APK_ORIGIN_DEFAULT = 0,
+ APK_ORIGIN_VENDOR = 1,
+ APK_ORIGIN_PRODUCT = 2,
+} ApkOrigin;
+
+jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
+ jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
+ jmethodID get_parent =
+ env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
+
+ return env->CallObjectMethod(class_loader, get_parent);
+}
+
+ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
+ ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
+
+ if (dex_path != nullptr) {
+ ScopedUtfChars dex_path_utf_chars(env, dex_path);
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
+ apk_origin = APK_ORIGIN_VENDOR;
+ }
+
+ if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
+ LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
+ "Dex path contains both vendor and product partition : %s",
+ dex_path_utf_chars.c_str());
+
+ apk_origin = APK_ORIGIN_PRODUCT;
+ }
+ }
+ return apk_origin;
+}
+
+} // namespace
+
+void LibraryNamespaces::Initialize() {
+ // Once public namespace is initialized there is no
+ // point in running this code - it will have no effect
+ // on the current list of public libraries.
+ if (initialized_) {
+ return;
+ }
+
+ // android_init_namespaces() expects all the public libraries
+ // to be loaded so that they can be found by soname alone.
+ //
+ // TODO(dimitry): this is a bit misleading since we do not know
+ // if the vendor public library is going to be opened from /vendor/lib
+ // we might as well end up loading them from /system/lib or /product/lib
+ // For now we rely on CTS test to catch things like this but
+ // it should probably be addressed in the future.
+ for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+ LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+ "Error preloading public library %s: %s", soname.c_str(), dlerror());
+ }
+}
+
+Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared,
+ jstring dex_path,
+ jstring java_library_path,
+ jstring java_permitted_path) {
+ std::string library_path; // empty string by default.
+
+ if (java_library_path != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, java_library_path);
+ library_path = library_path_utf_chars.c_str();
+ }
+
+ ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path);
+
+ // (http://b/27588281) This is a workaround for apps using custom
+ // classloaders and calling System.load() with an absolute path which
+ // is outside of the classloader library search path.
+ //
+ // This part effectively allows such a classloader to access anything
+ // under /data and /mnt/expand
+ std::string permitted_path = kWhitelistedDirectories;
+
+ if (java_permitted_path != nullptr) {
+ ScopedUtfChars path(env, java_permitted_path);
+ if (path.c_str() != nullptr && path.size() > 0) {
+ permitted_path = permitted_path + ":" + path.c_str();
+ }
+ }
+
+ // Initialize the anonymous namespace with the first non-empty library path.
+ Result<void> ret;
+ if (!library_path.empty() && !initialized_ &&
+ !(ret = InitPublicNamespace(library_path.c_str()))) {
+ return ret.error();
+ }
+
+ LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr,
+ "There is already a namespace associated with this classloader");
+
+ std::string system_exposed_libraries = default_public_libraries();
+ const char* namespace_name = kClassloaderNamespaceName;
+ bool unbundled_vendor_or_product_app = false;
+ if ((apk_origin == APK_ORIGIN_VENDOR ||
+ (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+ !is_shared) {
+ unbundled_vendor_or_product_app = true;
+ // For vendor / product apks, give access to the vendor / product lib even though
+ // they are treated as unbundled; the libs and apks are still bundled
+ // together in the vendor / product partition.
+ const char* origin_partition;
+ const char* origin_lib_path;
+
+ switch (apk_origin) {
+ case APK_ORIGIN_VENDOR:
+ origin_partition = "vendor";
+ origin_lib_path = kVendorLibPath;
+ break;
+ case APK_ORIGIN_PRODUCT:
+ origin_partition = "product";
+ origin_lib_path = kProductLibPath;
+ break;
+ default:
+ origin_partition = "unknown";
+ origin_lib_path = "";
+ }
+ library_path = library_path + ":" + origin_lib_path;
+ permitted_path = permitted_path + ":" + origin_lib_path;
+
+ // Also give access to LLNDK libraries since they are available to vendors
+ system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
+ origin_partition, library_path.c_str());
+ } else {
+ // extended public libraries are NOT available to vendor apks, otherwise it
+ // would be system->vendor violation.
+ if (!extended_public_libraries().empty()) {
+ system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
+ }
+ }
+
+ // Create the app namespace
+ NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
+ auto app_ns =
+ NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
+ is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
+ if (!app_ns) {
+ return app_ns.error();
+ }
+
+ // ... and link to other namespaces to allow access to some public libraries
+ bool is_bridged = app_ns->IsBridged();
+
+ auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
+ if (!platform_ns) {
+ return platform_ns.error();
+ }
+
+ auto linked = app_ns->Link(*platform_ns, system_exposed_libraries);
+ if (!linked) {
+ return linked.error();
+ }
+
+ auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
+ // Runtime apex does not exist in host, and under certain build conditions.
+ if (runtime_ns) {
+ linked = app_ns->Link(*runtime_ns, runtime_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+
+ // Give access to NNAPI libraries (apex-updated LLNDK library).
+ auto nnapi_ns =
+ NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged);
+ if (nnapi_ns) {
+ linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+
+ // Give access to VNDK-SP libraries from the 'vndk' namespace.
+ if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
+ auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
+ if (vndk_ns) {
+ linked = app_ns->Link(*vndk_ns, vndksp_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+ }
+
+ if (!vendor_public_libraries().empty()) {
+ auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
+ // when vendor_ns is not configured, link to the platform namespace
+ auto target_ns = vendor_ns ? vendor_ns : platform_ns;
+ if (target_ns) {
+ linked = app_ns->Link(*target_ns, vendor_public_libraries());
+ if (!linked) {
+ return linked.error();
+ }
+ }
+ }
+
+ namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
+
+ return &(namespaces_.back().second);
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+ [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+ return env->IsSameObject(value.first, class_loader);
+ });
+ if (it != namespaces_.end()) {
+ return &it->second;
+ }
+
+ return nullptr;
+}
+
+Result<void> LibraryNamespaces::InitPublicNamespace(const char* library_path) {
+ // Ask native bride if this apps library path should be handled by it
+ bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
+
+ // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+ // code is one example) unknown to linker in which case linker uses anonymous
+ // namespace. The second argument specifies the search path for the anonymous
+ // namespace which is the library_path of the classloader.
+ initialized_ = android_init_anonymous_namespace(default_public_libraries().c_str(),
+ is_native_bridge ? nullptr : library_path);
+ if (!initialized_) {
+ return Error() << dlerror();
+ }
+
+ // and now initialize native bridge namespaces if necessary.
+ if (NativeBridgeInitialized()) {
+ initialized_ = NativeBridgeInitAnonymousNamespace(default_public_libraries().c_str(),
+ is_native_bridge ? library_path : nullptr);
+ if (!initialized_) {
+ return Error() << NativeBridgeGetError();
+ }
+ }
+
+ return {};
+}
+
+NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ jobject parent_class_loader = GetParentClassLoader(env, class_loader);
+
+ while (parent_class_loader != nullptr) {
+ NativeLoaderNamespace* ns;
+ if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
+ return ns;
+ }
+
+ parent_class_loader = GetParentClassLoader(env, parent_class_loader);
+ }
+
+ return nullptr;
+}
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
new file mode 100644
index 0000000..e54bc0a
--- /dev/null
+++ b/libnativeloader/library_namespaces.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#if !defined(__ANDROID__)
+#error "Not available for host"
+#endif
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <list>
+#include <string>
+
+#include <android-base/result.h>
+#include <jni.h>
+
+namespace android::nativeloader {
+
+using android::base::Result;
+
+// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
+// objects for an app process. Its main job is to create (and configure) a new
+// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
+// object for a given ClassLoader.
+class LibraryNamespaces {
+ public:
+ LibraryNamespaces() : initialized_(false) {}
+
+ LibraryNamespaces(LibraryNamespaces&&) = default;
+ LibraryNamespaces(const LibraryNamespaces&) = delete;
+ LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
+
+ void Initialize();
+ void Reset() {
+ namespaces_.clear();
+ initialized_ = false;
+ }
+ Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version,
+ jobject class_loader, bool is_shared, jstring dex_path,
+ jstring java_library_path, jstring java_permitted_path);
+ NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ private:
+ Result<void> InitPublicNamespace(const char* library_path);
+ NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+
+ bool initialized_;
+ std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+};
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index b3e2b97..6d3c057 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -14,22 +14,13 @@
* limitations under the License.
*/
+#define LOG_TAG "nativeloader"
+
#include "nativeloader/native_loader.h"
-#include <nativehelper/ScopedUtfChars.h>
#include <dlfcn.h>
-#ifdef __ANDROID__
-#define LOG_TAG "libnativeloader"
-#include "nativeloader/dlext_namespaces.h"
-#include "cutils/properties.h"
-#include "log/log.h"
-#endif
-#include <dirent.h>
#include <sys/types.h>
-#include "nativebridge/native_bridge.h"
-#include <algorithm>
-#include <list>
#include <memory>
#include <mutex>
#include <string>
@@ -38,552 +29,49 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
+#include <nativebridge/native_bridge.h>
+#include <nativehelper/ScopedUtfChars.h>
-#ifdef __BIONIC__
-#include <android-base/properties.h>
+#ifdef __ANDROID__
+#include <log/log.h>
+#include "library_namespaces.h"
+#include "nativeloader/dlext_namespaces.h"
#endif
-#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
- "%s:%d: %s CHECK '" #predicate "' failed.",\
- __FILE__, __LINE__, __FUNCTION__)
-
-using namespace std::string_literals;
-
namespace android {
+namespace {
#if defined(__ANDROID__)
-class NativeLoaderNamespace {
- public:
- NativeLoaderNamespace()
- : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+using android::nativeloader::LibraryNamespaces;
- explicit NativeLoaderNamespace(android_namespace_t* ns)
- : android_ns_(ns), native_bridge_ns_(nullptr) { }
+constexpr const char* kApexPath = "/apex/";
- explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
- : android_ns_(nullptr), native_bridge_ns_(ns) { }
+std::mutex g_namespaces_mutex;
+LibraryNamespaces* g_namespaces = new LibraryNamespaces;
- NativeLoaderNamespace(NativeLoaderNamespace&& that) = default;
- NativeLoaderNamespace(const NativeLoaderNamespace& that) = default;
-
- NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default;
-
- android_namespace_t* get_android_ns() const {
- CHECK(native_bridge_ns_ == nullptr);
- return android_ns_;
+android_namespace_t* FindExportedNamespace(const char* caller_location) {
+ std::string location = caller_location;
+ // Lots of implicit assumptions here: we expect `caller_location` to be of the form:
+ // /apex/com.android...modulename/...
+ //
+ // And we extract from it 'modulename', which is the name of the linker namespace.
+ if (android::base::StartsWith(location, kApexPath)) {
+ size_t slash_index = location.find_first_of('/', strlen(kApexPath));
+ LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos),
+ "Error finding namespace of apex: no slash in path %s", caller_location);
+ size_t dot_index = location.find_last_of('.', slash_index);
+ LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos),
+ "Error finding namespace of apex: no dot in apex name %s", caller_location);
+ std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1);
+ android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str());
+ LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
+ "Error finding namespace of apex: no namespace called %s", name.c_str());
+ return boot_namespace;
}
-
- native_bridge_namespace_t* get_native_bridge_ns() const {
- CHECK(android_ns_ == nullptr);
- return native_bridge_ns_;
- }
-
- bool is_android_namespace() const {
- return native_bridge_ns_ == nullptr;
- }
-
- private:
- // Only one of them can be not null
- android_namespace_t* android_ns_;
- native_bridge_namespace_t* native_bridge_ns_;
-};
-
-static constexpr const char kPublicNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/public.libraries.txt";
-static constexpr const char kPublicNativeLibrariesExtensionConfigPrefix[] = "public.libraries-";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigPrefixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigPrefix) - 1;
-static constexpr const char kPublicNativeLibrariesExtensionConfigSuffix[] = ".txt";
-static constexpr const size_t kPublicNativeLibrariesExtensionConfigSuffixLen =
- sizeof(kPublicNativeLibrariesExtensionConfigSuffix) - 1;
-static constexpr const char kPublicNativeLibrariesVendorConfig[] =
- "/vendor/etc/public.libraries.txt";
-static constexpr const char kLlndkNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/llndk.libraries.txt";
-static constexpr const char kVndkspNativeLibrariesSystemConfigPathFromRoot[] =
- "/etc/vndksp.libraries.txt";
-
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-static constexpr const char* kVendorNamespaceName = "sphal";
-
-static constexpr const char* kVndkNamespaceName = "vndk";
-
-static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-static constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-static bool is_debuggable() {
- char debuggable[PROP_VALUE_MAX];
- property_get("ro.debuggable", debuggable, "0");
- return std::string(debuggable) == "1";
+ return nullptr;
}
-
-static std::string vndk_version_str() {
-#ifdef __BIONIC__
- std::string version = android::base::GetProperty("ro.vndk.version", "");
- if (version != "" && version != "current") {
- return "." + version;
- }
-#endif
- return "";
-}
-
-static void insert_vndk_version_str(std::string* file_name) {
- CHECK(file_name != nullptr);
- size_t insert_pos = file_name->find_last_of(".");
- if (insert_pos == std::string::npos) {
- insert_pos = file_name->length();
- }
- file_name->insert(insert_pos, vndk_version_str());
-}
-
-static const std::function<bool(const std::string&, std::string*)> always_true =
- [](const std::string&, std::string*) { return true; };
-
-class LibraryNamespaces {
- public:
- LibraryNamespaces() : initialized_(false) { }
-
- NativeLoaderNamespace* Create(JNIEnv* env,
- uint32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring java_library_path,
- jstring java_permitted_path,
- std::string* error_msg) {
- std::string library_path; // empty string by default.
-
- if (java_library_path != nullptr) {
- ScopedUtfChars library_path_utf_chars(env, java_library_path);
- library_path = library_path_utf_chars.c_str();
- }
-
- // (http://b/27588281) This is a workaround for apps using custom
- // classloaders and calling System.load() with an absolute path which
- // is outside of the classloader library search path.
- //
- // This part effectively allows such a classloader to access anything
- // under /data and /mnt/expand
- std::string permitted_path = kWhitelistedDirectories;
-
- if (java_permitted_path != nullptr) {
- ScopedUtfChars path(env, java_permitted_path);
- if (path.c_str() != nullptr && path.size() > 0) {
- permitted_path = permitted_path + ":" + path.c_str();
- }
- }
-
- if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) {
- return nullptr;
- }
-
- bool found = FindNamespaceByClassLoader(env, class_loader);
-
- LOG_ALWAYS_FATAL_IF(found,
- "There is already a namespace associated with this classloader");
-
- uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
- if (is_shared) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
- }
-
- if (target_sdk_version < 24) {
- namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
- }
-
- NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-
- bool is_native_bridge = false;
-
- if (parent_ns != nullptr) {
- is_native_bridge = !parent_ns->is_android_namespace();
- } else if (!library_path.empty()) {
- is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
- }
-
- std::string system_exposed_libraries = system_public_libraries_;
- const char* namespace_name = kClassloaderNamespaceName;
- android_namespace_t* vndk_ns = nullptr;
- if (is_for_vendor && !is_shared) {
- LOG_FATAL_IF(is_native_bridge, "Unbundled vendor apk must not use translated architecture");
-
- // For vendor apks, give access to the vendor lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor partition.
-#if defined(__LP64__)
- std::string vendor_lib_path = "/vendor/lib64";
-#else
- std::string vendor_lib_path = "/vendor/lib";
-#endif
- library_path = library_path + ":" + vendor_lib_path.c_str();
- permitted_path = permitted_path + ":" + vendor_lib_path.c_str();
-
- // Also give access to LLNDK libraries since they are available to vendors
- system_exposed_libraries = system_exposed_libraries + ":" + system_llndk_libraries_.c_str();
-
- // Give access to VNDK-SP libraries from the 'vndk' namespace.
- vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
- LOG_ALWAYS_FATAL_IF(vndk_ns == nullptr,
- "Cannot find \"%s\" namespace for vendor apks", kVndkNamespaceName);
-
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled vendor apk. library_path=%s", library_path.c_str());
- } else {
- // oem and product public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!oem_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + oem_public_libraries_;
- }
- if (!product_public_libraries_.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + product_public_libraries_;
- }
- }
-
- NativeLoaderNamespace native_loader_ns;
- if (!is_native_bridge) {
- android_namespace_t* android_parent_ns =
- parent_ns == nullptr ? nullptr : parent_ns->get_android_ns();
- android_namespace_t* ns = android_create_namespace(namespace_name,
- nullptr,
- library_path.c_str(),
- namespace_type,
- permitted_path.c_str(),
- android_parent_ns);
- if (ns == nullptr) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- // Note that when vendor_ns is not configured this function will return nullptr
- // and it will result in linking vendor_public_libraries_ to the default namespace
- // which is expected behavior in this case.
- android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
-
- if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
-
- if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
- // vendor apks are allowed to use VNDK-SP libraries.
- if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- if (!vendor_public_libraries_.empty()) {
- if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = dlerror();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- } else {
- native_bridge_namespace_t* native_bridge_parent_namespace =
- parent_ns == nullptr ? nullptr : parent_ns->get_native_bridge_ns();
- native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
- nullptr,
- library_path.c_str(),
- namespace_type,
- permitted_path.c_str(),
- native_bridge_parent_namespace);
- if (ns == nullptr) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- native_bridge_namespace_t* vendor_ns = NativeBridgeGetVendorNamespace();
-
- if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
-
- if (!vendor_public_libraries_.empty()) {
- if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
- *error_msg = NativeBridgeGetError();
- return nullptr;
- }
- }
-
- native_loader_ns = NativeLoaderNamespace(ns);
- }
-
- namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
-
- return &(namespaces_.back().second);
- }
-
- NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
- [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
- return env->IsSameObject(value.first, class_loader);
- });
- if (it != namespaces_.end()) {
- return &it->second;
- }
-
- return nullptr;
- }
-
- void Initialize() {
- // Once public namespace is initialized there is no
- // point in running this code - it will have no effect
- // on the current list of public libraries.
- if (initialized_) {
- return;
- }
-
- std::vector<std::string> sonames;
- const char* android_root_env = getenv("ANDROID_ROOT");
- std::string root_dir = android_root_env != nullptr ? android_root_env : "/system";
- std::string public_native_libraries_system_config =
- root_dir + kPublicNativeLibrariesSystemConfigPathFromRoot;
- std::string llndk_native_libraries_system_config =
- root_dir + kLlndkNativeLibrariesSystemConfigPathFromRoot;
- std::string vndksp_native_libraries_system_config =
- root_dir + kVndkspNativeLibrariesSystemConfigPathFromRoot;
-
- std::string product_public_native_libraries_dir = "/product/etc";
-
- std::string error_msg;
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(public_native_libraries_system_config, &sonames, always_true, &error_msg),
- "Error reading public native library list from \"%s\": %s",
- public_native_libraries_system_config.c_str(), error_msg.c_str());
-
- // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
- // variable to add libraries to the list. This is intended for platform tests only.
- if (is_debuggable()) {
- const char* additional_libs = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
- if (additional_libs != nullptr && additional_libs[0] != '\0') {
- std::vector<std::string> additional_libs_vector = base::Split(additional_libs, ":");
- std::copy(additional_libs_vector.begin(), additional_libs_vector.end(),
- std::back_inserter(sonames));
- }
- }
-
- // android_init_namespaces() expects all the public libraries
- // to be loaded so that they can be found by soname alone.
- //
- // TODO(dimitry): this is a bit misleading since we do not know
- // if the vendor public library is going to be opened from /vendor/lib
- // we might as well end up loading them from /system/lib or /product/lib
- // For now we rely on CTS test to catch things like this but
- // it should probably be addressed in the future.
- for (const auto& soname : sonames) {
- LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
- "Error preloading public library %s: %s", soname.c_str(), dlerror());
- }
-
- system_public_libraries_ = base::Join(sonames, ':');
-
- // read /system/etc/public.libraries-<companyname>.txt which contain partner defined
- // system libs that are exposed to apps. The libs in the txt files must be
- // named as lib<name>.<companyname>.so.
- sonames.clear();
- ReadExtensionLibraries(base::Dirname(public_native_libraries_system_config).c_str(), &sonames);
- oem_public_libraries_ = base::Join(sonames, ':');
-
- // read /product/etc/public.libraries-<companyname>.txt which contain partner defined
- // product libs that are exposed to apps.
- sonames.clear();
- ReadExtensionLibraries(product_public_native_libraries_dir.c_str(), &sonames);
- product_public_libraries_ = base::Join(sonames, ':');
-
- // Insert VNDK version to llndk and vndksp config file names.
- insert_vndk_version_str(&llndk_native_libraries_system_config);
- insert_vndk_version_str(&vndksp_native_libraries_system_config);
-
- sonames.clear();
- ReadConfig(llndk_native_libraries_system_config, &sonames, always_true);
- system_llndk_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- ReadConfig(vndksp_native_libraries_system_config, &sonames, always_true);
- system_vndksp_libraries_ = base::Join(sonames, ':');
-
- sonames.clear();
- // This file is optional, quietly ignore if the file does not exist.
- ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames, always_true, nullptr);
-
- vendor_public_libraries_ = base::Join(sonames, ':');
- }
-
- void Reset() { namespaces_.clear(); }
-
- private:
- void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- while (struct dirent* ent = readdir(dir.get())) {
- if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
- continue;
- }
- const std::string filename(ent->d_name);
- if (android::base::StartsWith(filename, kPublicNativeLibrariesExtensionConfigPrefix) &&
- android::base::EndsWith(filename, kPublicNativeLibrariesExtensionConfigSuffix)) {
- const size_t start = kPublicNativeLibrariesExtensionConfigPrefixLen;
- const size_t end = filename.size() - kPublicNativeLibrariesExtensionConfigSuffixLen;
- const std::string company_name = filename.substr(start, end - start);
- const std::string config_file_path = dirname + "/"s + filename;
- LOG_ALWAYS_FATAL_IF(
- company_name.empty(),
- "Error extracting company name from public native library list file path \"%s\"",
- config_file_path.c_str());
-
- std::string error_msg;
-
- LOG_ALWAYS_FATAL_IF(
- !ReadConfig(
- config_file_path, sonames,
- [&company_name](const std::string& soname, std::string* error_msg) {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return true;
- } else {
- *error_msg = "Library name \"" + soname +
- "\" does not end with the company name: " + company_name + ".";
- return false;
- }
- },
- &error_msg),
- "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
- error_msg.c_str());
- }
- }
- }
- }
-
-
- bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
- const std::function<bool(const std::string& /* soname */,
- std::string* /* error_msg */)>& check_soname,
- std::string* error_msg = nullptr) {
- // Read list of public native libraries from the config file.
- std::string file_content;
- if(!base::ReadFileToString(configFile, &file_content)) {
- if (error_msg) *error_msg = strerror(errno);
- return false;
- }
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- if (error_msg) *error_msg = "Malformed line: " + line;
- return false;
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- if (check_soname(trimmed_line, error_msg)) {
- sonames->push_back(trimmed_line);
- } else {
- return false;
- }
- }
-
- return true;
- }
-
- bool InitPublicNamespace(const char* library_path, std::string* error_msg) {
- // Ask native bride if this apps library path should be handled by it
- bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
- // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
- // code is one example) unknown to linker in which case linker uses anonymous
- // namespace. The second argument specifies the search path for the anonymous
- // namespace which is the library_path of the classloader.
- initialized_ = android_init_anonymous_namespace(system_public_libraries_.c_str(),
- is_native_bridge ? nullptr : library_path);
- if (!initialized_) {
- *error_msg = dlerror();
- return false;
- }
-
- // and now initialize native bridge namespaces if necessary.
- if (NativeBridgeInitialized()) {
- initialized_ = NativeBridgeInitAnonymousNamespace(system_public_libraries_.c_str(),
- is_native_bridge ? library_path : nullptr);
- if (!initialized_) {
- *error_msg = NativeBridgeGetError();
- }
- }
-
- return initialized_;
- }
-
- jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
- jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
- jmethodID get_parent = env->GetMethodID(class_loader_class,
- "getParent",
- "()Ljava/lang/ClassLoader;");
-
- return env->CallObjectMethod(class_loader, get_parent);
- }
-
- NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
- jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
- while (parent_class_loader != nullptr) {
- NativeLoaderNamespace* ns;
- if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
- return ns;
- }
-
- parent_class_loader = GetParentClassLoader(env, parent_class_loader);
- }
-
- return nullptr;
- }
-
- bool initialized_;
- std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
- std::string system_public_libraries_;
- std::string vendor_public_libraries_;
- std::string oem_public_libraries_;
- std::string product_public_libraries_;
- std::string system_llndk_libraries_;
- std::string system_vndksp_libraries_;
-
- DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
-};
-
-static std::mutex g_namespaces_mutex;
-static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-#endif
+#endif // #if defined(__ANDROID__)
+} // namespace
void InitializeNativeLoader() {
#if defined(__ANDROID__)
@@ -599,47 +87,48 @@
#endif
}
-jstring CreateClassLoaderNamespace(JNIEnv* env,
- int32_t target_sdk_version,
- jobject class_loader,
- bool is_shared,
- bool is_for_vendor,
- jstring library_path,
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
jstring permitted_path) {
#if defined(__ANDROID__)
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-
- std::string error_msg;
- bool success = g_namespaces->Create(env,
- target_sdk_version,
- class_loader,
- is_shared,
- is_for_vendor,
- library_path,
- permitted_path,
- &error_msg) != nullptr;
- if (!success) {
- return env->NewStringUTF(error_msg.c_str());
+ auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
+ library_path, permitted_path);
+ if (!ns) {
+ return env->NewStringUTF(ns.error().message().c_str());
}
#else
- UNUSED(env, target_sdk_version, class_loader, is_shared, is_for_vendor,
- library_path, permitted_path);
+ UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
#endif
return nullptr;
}
-void* OpenNativeLibrary(JNIEnv* env,
- int32_t target_sdk_version,
- const char* path,
- jobject class_loader,
- jstring library_path,
- bool* needs_native_bridge,
- std::string* error_msg) {
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, const char* caller_location, jstring library_path,
+ bool* needs_native_bridge, char** error_msg) {
#if defined(__ANDROID__)
UNUSED(target_sdk_version);
if (class_loader == nullptr) {
*needs_native_bridge = false;
- return dlopen(path, RTLD_NOW);
+ if (caller_location != nullptr) {
+ android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
+ if (boot_namespace != nullptr) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = boot_namespace,
+ };
+ void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
+ if (handle == nullptr) {
+ *error_msg = strdup(dlerror());
+ }
+ return handle;
+ }
+ }
+ void* handle = dlopen(path, RTLD_NOW);
+ if (handle == nullptr) {
+ *error_msg = strdup(dlerror());
+ }
+ return handle;
}
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
@@ -648,21 +137,20 @@
if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
// This is the case where the classloader was not created by ApplicationLoaders
// In this case we create an isolated not-shared namespace for it.
- if ((ns = g_namespaces->Create(env,
- target_sdk_version,
- class_loader,
- false /* is_shared */,
- false /* is_for_vendor */,
- library_path,
- nullptr,
- error_msg)) == nullptr) {
+ Result<NativeLoaderNamespace*> isolated_ns =
+ g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr,
+ library_path, nullptr);
+ if (!isolated_ns) {
+ *error_msg = strdup(isolated_ns.error().message().c_str());
return nullptr;
+ } else {
+ ns = *isolated_ns;
}
}
- return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg);
+ return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
#else
- UNUSED(env, target_sdk_version, class_loader);
+ UNUSED(env, target_sdk_version, class_loader, caller_location);
// Do some best effort to emulate library-path support. It will not
// work for dependencies.
@@ -701,54 +189,48 @@
if (handle != nullptr) {
return handle;
}
- *error_msg = NativeBridgeGetError();
+ *error_msg = strdup(NativeBridgeGetError());
} else {
- *error_msg = dlerror();
+ *error_msg = strdup(dlerror());
}
}
return nullptr;
#endif
}
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, std::string* error_msg) {
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
bool success;
if (needs_native_bridge) {
success = (NativeBridgeUnloadLibrary(handle) == 0);
if (!success) {
- *error_msg = NativeBridgeGetError();
+ *error_msg = strdup(NativeBridgeGetError());
}
} else {
success = (dlclose(handle) == 0);
if (!success) {
- *error_msg = dlerror();
+ *error_msg = strdup(dlerror());
}
}
return success;
}
-#if defined(__ANDROID__)
-void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge,
- std::string* error_msg) {
- if (ns->is_android_namespace()) {
- android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
- extinfo.library_namespace = ns->get_android_ns();
+void NativeLoaderFreeErrorMessage(char* msg) {
+ // The error messages get allocated through strdup, so we must call free on them.
+ free(msg);
+}
- void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
- if (handle == nullptr) {
- *error_msg = dlerror();
- }
- *needs_native_bridge = false;
- return handle;
- } else {
- void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
- if (handle == nullptr) {
- *error_msg = NativeBridgeGetError();
- }
- *needs_native_bridge = true;
- return handle;
+#if defined(__ANDROID__)
+void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
+ bool* needs_native_bridge, char** error_msg) {
+ auto handle = ns->Load(path);
+ if (!handle && error_msg != nullptr) {
+ *error_msg = strdup(handle.error().message().c_str());
}
+ if (needs_native_bridge != nullptr) {
+ *needs_native_bridge = ns->IsBridged();
+ }
+ return handle ? *handle : nullptr;
}
// native_bridge_namespaces are not supported for callers of this function.
@@ -757,16 +239,16 @@
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
- if (ns != nullptr) {
- return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
+ if (ns != nullptr && !ns->IsBridged()) {
+ return ns->ToRawAndroidNamespace();
}
-
return nullptr;
}
+
NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
}
#endif
-}; // android namespace
+}; // namespace android
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
new file mode 100644
index 0000000..2eb1203
--- /dev/null
+++ b/libnativeloader/native_loader_lazy.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nativeloader/native_loader.h"
+#define LOG_TAG "nativeloader"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include <log/log.h>
+
+namespace android {
+
+namespace {
+
+void* GetLibHandle() {
+ static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
+ LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
+ return handle;
+}
+
+template <typename FuncPtr>
+FuncPtr GetFuncPtr(const char* function_name) {
+ auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
+ LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
+ return f;
+}
+
+#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
+
+} // namespace
+
+void InitializeNativeLoader() {
+ static auto f = GET_FUNC_PTR(InitializeNativeLoader);
+ return f();
+}
+
+jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
+ bool is_shared, jstring dex_path, jstring library_path,
+ jstring permitted_path) {
+ static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
+ return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
+ permitted_path);
+}
+
+void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
+ jobject class_loader, const char* caller_location, jstring library_path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibrary);
+ return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
+ needs_native_bridge, error_msg);
+}
+
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(CloseNativeLibrary);
+ return f(handle, needs_native_bridge, error_msg);
+}
+
+void NativeLoaderFreeErrorMessage(char* msg) {
+ static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
+ return f(msg);
+}
+
+struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
+ jobject class_loader) {
+ static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
+ return f(env, class_loader);
+}
+
+void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
+ bool* needs_native_bridge, char** error_msg) {
+ static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
+ return f(ns, path, needs_native_bridge, error_msg);
+}
+
+void ResetNativeLoader() {
+ static auto f = GET_FUNC_PTR(ResetNativeLoader);
+ return f();
+}
+
+#undef GET_FUNC_PTR
+
+} // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
new file mode 100644
index 0000000..4add6e6
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "native_loader_namespace.h"
+
+#include <dlfcn.h>
+
+#include <functional>
+
+#include <android-base/strings.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+#include "nativeloader/dlext_namespaces.h"
+
+using android::base::Error;
+using android::base::Errorf;
+
+namespace android {
+
+namespace {
+
+constexpr const char* kDefaultNamespaceName = "default";
+constexpr const char* kPlatformNamespaceName = "platform";
+
+std::string GetLinkerError(bool is_bridged) {
+ const char* msg = is_bridged ? NativeBridgeGetError() : dlerror();
+ if (msg == nullptr) {
+ return "no error";
+ }
+ return std::string(msg);
+}
+
+} // namespace
+
+Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
+ bool is_bridged) {
+ if (!is_bridged) {
+ auto raw = android_get_exported_namespace(name.c_str());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ } else {
+ auto raw = NativeBridgeGetExportedNamespace(name.c_str());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ }
+ return Errorf("namespace {} does not exist or exported", name);
+}
+
+// The platform namespace is called "default" for binaries in /system and
+// "platform" for those in the Runtime APEX. Try "platform" first since
+// "default" always exists.
+Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
+ auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
+ if (ns) return ns;
+ ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+ if (ns) return ns;
+
+ // If nothing is found, return NativeLoaderNamespace constructed from nullptr.
+ // nullptr also means default namespace to the linker.
+ if (!is_bridged) {
+ return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr));
+ } else {
+ return NativeLoaderNamespace(kDefaultNamespaceName,
+ static_cast<native_bridge_namespace_t*>(nullptr));
+ }
+}
+
+Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
+ const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled) {
+ bool is_bridged = false;
+ if (parent != nullptr) {
+ is_bridged = parent->IsBridged();
+ } else if (!search_paths.empty()) {
+ is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
+ }
+
+ // Fall back to the platform namespace if no parent is set.
+ auto platform_ns = GetPlatformNamespace(is_bridged);
+ if (!platform_ns) {
+ return platform_ns.error();
+ }
+ const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns;
+
+ uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ if (is_shared) {
+ type |= ANDROID_NAMESPACE_TYPE_SHARED;
+ }
+ if (is_greylist_enabled) {
+ type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
+ }
+
+ if (!is_bridged) {
+ android_namespace_t* raw =
+ android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
+ permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ } else {
+ native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
+ name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
+ effective_parent.ToRawNativeBridgeNamespace());
+ if (raw != nullptr) {
+ return NativeLoaderNamespace(name, raw);
+ }
+ }
+ return Errorf("failed to create {} namespace name:{}, search_paths:{}, permitted_paths:{}",
+ is_bridged ? "bridged" : "native", name, search_paths, permitted_paths);
+}
+
+Result<void> NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
+ const std::string& shared_libs) const {
+ LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
+ this->name().c_str(), target.name().c_str());
+ if (!IsBridged()) {
+ if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
+ shared_libs.c_str())) {
+ return {};
+ }
+ } else {
+ if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
+ target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) {
+ return {};
+ }
+ }
+ return Error() << GetLinkerError(IsBridged());
+}
+
+Result<void*> NativeLoaderNamespace::Load(const char* lib_name) const {
+ if (!IsBridged()) {
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = this->ToRawAndroidNamespace();
+ void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+ if (handle != nullptr) {
+ return handle;
+ }
+ } else {
+ void* handle =
+ NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
+ if (handle != nullptr) {
+ return handle;
+ }
+ }
+ return Error() << GetLinkerError(IsBridged());
+}
+
+} // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
new file mode 100644
index 0000000..29b759c
--- /dev/null
+++ b/libnativeloader/native_loader_namespace.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#if defined(__ANDROID__)
+
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <android/dlext.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+
+namespace android {
+
+using android::base::Result;
+
+// NativeLoaderNamespace abstracts a linker namespace for the native
+// architecture (ex: arm on arm) or the translated architecture (ex: arm on
+// x86). Instances of this class are managed by LibraryNamespaces object.
+struct NativeLoaderNamespace {
+ public:
+ static Result<NativeLoaderNamespace> Create(const std::string& name,
+ const std::string& search_paths,
+ const std::string& permitted_paths,
+ const NativeLoaderNamespace* parent, bool is_shared,
+ bool is_greylist_enabled);
+
+ NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
+ NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
+ NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
+
+ android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
+ native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
+
+ std::string name() const { return name_; }
+ bool IsBridged() const { return raw_.index() == 1; }
+
+ Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
+ Result<void*> Load(const char* lib_name) const;
+
+ static Result<NativeLoaderNamespace> GetExportedNamespace(const std::string& name,
+ bool is_bridged);
+ static Result<NativeLoaderNamespace> GetPlatformNamespace(bool is_bridged);
+
+ private:
+ explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+ explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
+ : name_(name), raw_(ns) {}
+
+ std::string name_;
+ std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
+};
+
+} // namespace android
+#endif // #if defined(__ANDROID__)
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
new file mode 100644
index 0000000..b939eee
--- /dev/null
+++ b/libnativeloader/native_loader_test.cpp
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <memory>
+#include <unordered_map>
+
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <jni.h>
+
+#include "native_loader_namespace.h"
+#include "nativeloader/dlext_namespaces.h"
+#include "nativeloader/native_loader.h"
+#include "public_libraries.h"
+
+using namespace ::testing;
+
+namespace android {
+namespace nativeloader {
+
+// gmock interface that represents interested platform APIs on libdl and libnativebridge
+class Platform {
+ public:
+ virtual ~Platform() {}
+
+ // libdl APIs
+ virtual void* dlopen(const char* filename, int flags) = 0;
+ virtual int dlclose(void* handle) = 0;
+ virtual char* dlerror(void) = 0;
+
+ // These mock_* are the APIs semantically the same across libdl and libnativebridge.
+ // Instead of having two set of mock APIs for the two, define only one set with an additional
+ // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
+ typedef char* mock_namespace_handle;
+ virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
+ const char* search_paths) = 0;
+ virtual mock_namespace_handle mock_create_namespace(
+ bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
+ uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
+ virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
+ mock_namespace_handle to, const char* sonames) = 0;
+ virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
+ virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
+ mock_namespace_handle ns) = 0;
+
+ // libnativebridge APIs for which libdl has no corresponding APIs
+ virtual bool NativeBridgeInitialized() = 0;
+ virtual const char* NativeBridgeGetError() = 0;
+ virtual bool NativeBridgeIsPathSupported(const char*) = 0;
+ virtual bool NativeBridgeIsSupported(const char*) = 0;
+
+ // To mock "ClassLoader Object.getParent()"
+ virtual const char* JniObject_getParent(const char*) = 0;
+};
+
+// The mock does not actually create a namespace object. But simply casts the pointer to the
+// string for the namespace name as the handle to the namespace object.
+#define TO_ANDROID_NAMESPACE(str) \
+ reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
+
+#define TO_BRIDGED_NAMESPACE(str) \
+ reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
+
+#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
+
+// These represents built-in namespaces created by the linker according to ld.config.txt
+static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
+ {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
+ {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
+ {"runtime", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("runtime"))},
+ {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
+ {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
+ {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
+};
+
+// The actual gmock object
+class MockPlatform : public Platform {
+ public:
+ MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
+ ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
+ ON_CALL(*this, mock_get_exported_namespace(_, _))
+ .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle {
+ if (namespaces.find(name) != namespaces.end()) {
+ return namespaces[name];
+ }
+ return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
+ }));
+ }
+
+ // Mocking libdl APIs
+ MOCK_METHOD2(dlopen, void*(const char*, int));
+ MOCK_METHOD1(dlclose, int(void*));
+ MOCK_METHOD0(dlerror, char*());
+
+ // Mocking the common APIs
+ MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
+ MOCK_METHOD7(mock_create_namespace,
+ mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
+ const char*, mock_namespace_handle));
+ MOCK_METHOD4(mock_link_namespaces,
+ bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
+ MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
+ MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
+
+ // Mocking libnativebridge APIs
+ MOCK_METHOD0(NativeBridgeInitialized, bool());
+ MOCK_METHOD0(NativeBridgeGetError, const char*());
+ MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
+ MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
+
+ // Mocking "ClassLoader Object.getParent()"
+ MOCK_METHOD1(JniObject_getParent, const char*(const char*));
+
+ private:
+ bool is_bridged_;
+};
+
+static std::unique_ptr<MockPlatform> mock;
+
+// Provide C wrappers for the mock object.
+extern "C" {
+void* dlopen(const char* file, int flag) {
+ return mock->dlopen(file, flag);
+}
+
+int dlclose(void* handle) {
+ return mock->dlclose(handle);
+}
+
+char* dlerror(void) {
+ return mock->dlerror();
+}
+
+bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
+ return mock->mock_init_anonymous_namespace(false, sonames, search_path);
+}
+
+struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+ const char* default_library_path,
+ uint64_t type,
+ const char* permitted_when_isolated_path,
+ struct android_namespace_t* parent) {
+ return TO_ANDROID_NAMESPACE(
+ mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
+ const char* sonames) {
+ return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+struct android_namespace_t* android_get_exported_namespace(const char* name) {
+ return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
+}
+
+void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
+ return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
+}
+
+// libnativebridge APIs
+bool NativeBridgeIsSupported(const char* libpath) {
+ return mock->NativeBridgeIsSupported(libpath);
+}
+
+struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+ return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
+}
+
+struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
+ const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent) {
+ return TO_BRIDGED_NAMESPACE(
+ mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
+ permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
+}
+
+bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
+ struct native_bridge_namespace_t* to, const char* sonames) {
+ return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
+}
+
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+ struct native_bridge_namespace_t* ns) {
+ return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
+}
+
+bool NativeBridgeInitialized() {
+ return mock->NativeBridgeInitialized();
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+ const char* anon_ns_library_path) {
+ return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
+}
+
+const char* NativeBridgeGetError() {
+ return mock->NativeBridgeGetError();
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+ return mock->NativeBridgeIsPathSupported(path);
+}
+
+} // extern "C"
+
+// A very simple JNI mock.
+// jstring is a pointer to utf8 char array. We don't need utf16 char here.
+// jobject, jclass, and jmethodID are also a pointer to utf8 char array
+// Only a few JNI methods that are actually used in libnativeloader are mocked.
+JNINativeInterface* CreateJNINativeInterface() {
+ JNINativeInterface* inf = new JNINativeInterface();
+ memset(inf, 0, sizeof(JNINativeInterface));
+
+ inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
+ return reinterpret_cast<const char*>(s);
+ };
+
+ inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
+
+ inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
+ return reinterpret_cast<jstring>(const_cast<char*>(bytes));
+ };
+
+ inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
+ return reinterpret_cast<jclass>(const_cast<char*>(name));
+ };
+
+ inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
+ if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
+ // JniObject_getParent can be a valid jobject or nullptr if there is
+ // no parent classloader.
+ const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
+ return reinterpret_cast<jobject>(const_cast<char*>(ret));
+ }
+ return nullptr;
+ };
+
+ inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
+ return reinterpret_cast<jmethodID>(const_cast<char*>(name));
+ };
+
+ inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
+
+ inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
+ return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
+ };
+
+ return inf;
+}
+
+static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
+
+// Custom matcher for comparing namespace handles
+MATCHER_P(NsEq, other, "") {
+ *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
+ return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
+}
+
+/////////////////////////////////////////////////////////////////
+
+// Test fixture
+class NativeLoaderTest : public ::testing::TestWithParam<bool> {
+ protected:
+ bool IsBridged() { return GetParam(); }
+
+ void SetUp() override {
+ mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged());
+
+ env = std::make_unique<JNIEnv>();
+ env->functions = CreateJNINativeInterface();
+ }
+
+ void SetExpectations() {
+ std::vector<std::string> default_public_libs =
+ android::base::Split(default_public_libraries(), ":");
+ for (auto l : default_public_libs) {
+ EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
+ .WillOnce(Return(any_nonnull));
+ }
+ }
+
+ void RunTest() { InitializeNativeLoader(); }
+
+ void TearDown() override {
+ ResetNativeLoader();
+ delete env->functions;
+ mock.reset();
+ }
+
+ std::unique_ptr<JNIEnv> env;
+};
+
+/////////////////////////////////////////////////////////////////
+
+TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
+ SetExpectations();
+ RunTest();
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
+
+/////////////////////////////////////////////////////////////////
+
+class NativeLoaderTest_Create : public NativeLoaderTest {
+ protected:
+ // Test inputs (initialized to the default values). Overriding these
+ // must be done before calling SetExpectations() and RunTest().
+ uint32_t target_sdk_version = 29;
+ std::string class_loader = "my_classloader";
+ bool is_shared = false;
+ std::string dex_path = "/data/app/foo/classes.dex";
+ std::string library_path = "/data/app/foo/lib/arm";
+ std::string permitted_path = "/data/app/foo/lib";
+
+ // expected output (.. for the default test inputs)
+ std::string expected_namespace_name = "classloader-namespace";
+ uint64_t expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
+ std::string expected_library_path = library_path;
+ std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
+ std::string expected_parent_namespace = "platform";
+ bool expected_link_with_platform_ns = true;
+ bool expected_link_with_runtime_ns = true;
+ bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
+ bool expected_link_with_vndk_ns = false;
+ bool expected_link_with_default_ns = false;
+ bool expected_link_with_neuralnetworks_ns = true;
+ std::string expected_shared_libs_to_platform_ns = default_public_libraries();
+ std::string expected_shared_libs_to_runtime_ns = runtime_public_libraries();
+ std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
+ std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
+ std::string expected_shared_libs_to_default_ns = default_public_libraries();
+ std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
+
+ void SetExpectations() {
+ NativeLoaderTest::SetExpectations();
+
+ ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
+
+ EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber());
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber());
+
+ if (IsBridged()) {
+ EXPECT_CALL(*mock,
+ mock_init_anonymous_namespace(false, StrEq(default_public_libraries()), nullptr))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(*mock, NativeBridgeInitialized()).WillOnce(Return(true));
+ }
+
+ EXPECT_CALL(*mock, mock_init_anonymous_namespace(
+ Eq(IsBridged()), StrEq(default_public_libraries()), StrEq(library_path)))
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mock, mock_create_namespace(
+ Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(expected_library_path), expected_namespace_flags,
+ StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
+ if (expected_link_with_platform_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
+ StrEq(expected_shared_libs_to_platform_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_runtime_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("runtime"),
+ StrEq(expected_shared_libs_to_runtime_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_sphal_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
+ StrEq(expected_shared_libs_to_sphal_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_vndk_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
+ StrEq(expected_shared_libs_to_vndk_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_default_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
+ StrEq(expected_shared_libs_to_default_ns)))
+ .WillOnce(Return(true));
+ }
+ if (expected_link_with_neuralnetworks_ns) {
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
+ StrEq(expected_shared_libs_to_neuralnetworks_ns)))
+ .WillOnce(Return(true));
+ }
+ }
+
+ void RunTest() {
+ NativeLoaderTest::RunTest();
+
+ jstring err = CreateClassLoaderNamespace(
+ env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
+ env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
+ env()->NewStringUTF(permitted_path.c_str()));
+
+ // no error
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for this apk
+ EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns =
+ FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
+
+ // The created namespace is for the this apk
+ EXPECT_STREQ(dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+ }
+
+ JNIEnv* env() { return NativeLoaderTest::env.get(); }
+};
+
+TEST_P(NativeLoaderTest_Create, DownloadedApp) {
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
+ dex_path = "/system/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
+ dex_path = "/vendor/app/foo/foo.apk";
+ is_shared = false;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/vendor/lib";
+ expected_permitted_path = expected_permitted_path + ":/vendor/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = true;
+ target_sdk_version = 30;
+
+ expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
+ dex_path = "/product/app/foo/foo.apk";
+ is_shared = false;
+ target_sdk_version = 30;
+
+ expected_namespace_name = "vendor-classloader-namespace";
+ expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
+ expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
+ expected_shared_libs_to_platform_ns =
+ expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
+ expected_link_with_vndk_ns = true;
+ SetExpectations();
+ RunTest();
+}
+
+TEST_P(NativeLoaderTest_Create, TwoApks) {
+ SetExpectations();
+ const uint32_t second_app_target_sdk_version = 29;
+ const std::string second_app_class_loader = "second_app_classloader";
+ const bool second_app_is_shared = false;
+ const std::string second_app_dex_path = "/data/app/bar/classes.dex";
+ const std::string second_app_library_path = "/data/app/bar/lib/arm";
+ const std::string second_app_permitted_path = "/data/app/bar/lib";
+ const std::string expected_second_app_permitted_path =
+ std::string("/data:/mnt/expand:") + second_app_permitted_path;
+ const std::string expected_second_app_parent_namespace = "classloader-namespace";
+
+ // The scenario is that second app is loaded by the first app.
+ // So the first app's classloader (`classloader`) is parent of the second
+ // app's classloader.
+ ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
+ .WillByDefault(Return(class_loader.c_str()));
+
+ // namespace for the second app is created. Its parent is set to the namespace
+ // of the first app.
+ EXPECT_CALL(*mock, mock_create_namespace(Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ StrEq(second_app_library_path), expected_namespace_flags,
+ StrEq(expected_second_app_permitted_path),
+ NsEq(dex_path.c_str())))
+ .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
+ EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
+ .WillRepeatedly(Return(true));
+
+ RunTest();
+ jstring err = CreateClassLoaderNamespace(
+ env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
+ second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
+ env()->NewStringUTF(second_app_library_path.c_str()),
+ env()->NewStringUTF(second_app_permitted_path.c_str()));
+
+ // success
+ EXPECT_EQ(err, nullptr);
+
+ if (!IsBridged()) {
+ struct android_namespace_t* ns =
+ FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
+ } else {
+ struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
+ env(), env()->NewStringUTF(second_app_class_loader.c_str()));
+
+ // The created namespace is for the second apk
+ EXPECT_STREQ(second_app_dex_path.c_str(),
+ reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+
+// TODO(b/130388701#comment22) add a test for anonymous namespace
+
+} // namespace nativeloader
+} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
new file mode 100644
index 0000000..6cee668
--- /dev/null
+++ b/libnativeloader/public_libraries.cpp
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "nativeloader"
+
+#include "public_libraries.h"
+
+#include <dirent.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/result.h>
+#include <android-base/strings.h>
+#include <log/log.h>
+
+#include "utils.h"
+
+namespace android::nativeloader {
+
+using namespace std::string_literals;
+using android::base::ErrnoError;
+using android::base::Errorf;
+using android::base::Result;
+
+namespace {
+
+constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
+constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
+constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
+constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
+constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
+constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
+
+const std::vector<const std::string> kRuntimePublicLibraries = {
+ "libicuuc.so",
+ "libicui18n.so",
+};
+
+constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" LIB;
+
+constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
+
+// TODO(b/130388701): do we need this?
+std::string root_dir() {
+ static const char* android_root_env = getenv("ANDROID_ROOT");
+ return android_root_env != nullptr ? android_root_env : "/system";
+}
+
+bool debuggable() {
+ static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ return debuggable;
+}
+
+std::string vndk_version_str() {
+ static std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+ return "";
+}
+
+// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
+// variable to add libraries to the list. This is intended for platform tests only.
+std::string additional_public_libraries() {
+ if (debuggable()) {
+ const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
+ return val ? val : "";
+ }
+ return "";
+}
+
+void InsertVndkVersionStr(std::string* file_name) {
+ CHECK(file_name != nullptr);
+ size_t insert_pos = file_name->find_last_of(".");
+ if (insert_pos == std::string::npos) {
+ insert_pos = file_name->length();
+ }
+ file_name->insert(insert_pos, vndk_version_str());
+}
+
+const std::function<Result<void>(const std::string&)> always_true =
+ [](const std::string&) -> Result<void> { return {}; };
+
+Result<std::vector<std::string>> ReadConfig(
+ const std::string& configFile,
+ const std::function<Result<void>(const std::string& /* soname */)>& check_soname) {
+ // Read list of public native libraries from the config file.
+ std::string file_content;
+ if (!base::ReadFileToString(configFile, &file_content)) {
+ return ErrnoError();
+ }
+
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+ size_t space_pos = trimmed_line.rfind(' ');
+ if (space_pos != std::string::npos) {
+ std::string type = trimmed_line.substr(space_pos + 1);
+ if (type != "32" && type != "64") {
+ return Errorf("Malformed line: {}", line);
+ }
+#if defined(__LP64__)
+ // Skip 32 bit public library.
+ if (type == "32") {
+ continue;
+ }
+#else
+ // Skip 64 bit public library.
+ if (type == "64") {
+ continue;
+ }
+#endif
+ trimmed_line.resize(space_pos);
+ }
+
+ auto ret = check_soname(trimmed_line);
+ if (!ret) {
+ return ret.error();
+ }
+ sonames.push_back(trimmed_line);
+ }
+ return sonames;
+}
+
+void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
+ if (dir != nullptr) {
+ // Failing to opening the dir is not an error, which can happen in
+ // webview_zygote.
+ while (struct dirent* ent = readdir(dir.get())) {
+ if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
+ continue;
+ }
+ const std::string filename(ent->d_name);
+ std::string_view fn = filename;
+ if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
+ android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
+ const std::string company_name(fn);
+ const std::string config_file_path = dirname + "/"s + filename;
+ LOG_ALWAYS_FATAL_IF(
+ company_name.empty(),
+ "Error extracting company name from public native library list file path \"%s\"",
+ config_file_path.c_str());
+
+ auto ret = ReadConfig(
+ config_file_path, [&company_name](const std::string& soname) -> Result<void> {
+ if (android::base::StartsWith(soname, "lib") &&
+ android::base::EndsWith(soname, "." + company_name + ".so")) {
+ return {};
+ } else {
+ return Errorf("Library name \"{}\" does not end with the company name {}.", soname,
+ company_name);
+ }
+ });
+ if (ret) {
+ sonames->insert(sonames->end(), ret->begin(), ret->end());
+ } else {
+ LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
+ config_file_path.c_str(), ret.error().message().c_str());
+ }
+ }
+ }
+ }
+}
+
+static std::string InitDefaultPublicLibraries() {
+ std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
+ auto sonames = ReadConfig(config_file, always_true);
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
+ config_file.c_str(), sonames.error().message().c_str());
+ return "";
+ }
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ auto vec = base::Split(additional_libs, ":");
+ std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
+ }
+
+ // Remove the public libs in the runtime namespace.
+ // These libs are listed in public.android.txt, but we don't want the rest of android
+ // in default namespace to dlopen the libs.
+ // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
+ // Unfortunately, it does not have stable C symbols, and default namespace should only use
+ // stable symbols in libandroidicu.so. http://b/120786417
+ for (const std::string& lib_name : kRuntimePublicLibraries) {
+ std::string path(kRuntimeApexLibPath);
+ path.append("/").append(lib_name);
+
+ struct stat s;
+ // Do nothing if the path in /apex does not exist.
+ // Runtime APEX must be mounted since libnativeloader is in the same APEX
+ if (stat(path.c_str(), &s) != 0) {
+ continue;
+ }
+
+ auto it = std::find(sonames->begin(), sonames->end(), lib_name);
+ if (it != sonames->end()) {
+ sonames->erase(it);
+ }
+ }
+
+ // Remove the public libs in the nnapi namespace.
+ auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary);
+ if (it != sonames->end()) {
+ sonames->erase(it);
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitRuntimePublicLibraries() {
+ CHECK(sizeof(kRuntimePublicLibraries) > 0);
+ std::string list = android::base::Join(kRuntimePublicLibraries, ":");
+
+ std::string additional_libs = additional_public_libraries();
+ if (!additional_libs.empty()) {
+ list = list + ':' + additional_libs;
+ }
+ return list;
+}
+
+static std::string InitVendorPublicLibraries() {
+ // This file is optional, quietly ignore if the file does not exist.
+ auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
+ if (!sonames) {
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+// read /system/etc/public.libraries-<companyname>.txt and
+// /product/etc/public.libraries-<companyname>.txt which contain partner defined
+// system libs that are exposed to apps. The libs in the txt files must be
+// named as lib<name>.<companyname>.so.
+static std::string InitExtendedPublicLibraries() {
+ std::vector<std::string> sonames;
+ ReadExtensionLibraries("/system/etc", &sonames);
+ ReadExtensionLibraries("/product/etc", &sonames);
+ return android::base::Join(sonames, ':');
+}
+
+static std::string InitLlndkLibraries() {
+ std::string config_file = kLlndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ auto sonames = ReadConfig(config_file, always_true);
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitVndkspLibraries() {
+ std::string config_file = kVndkLibrariesFile;
+ InsertVndkVersionStr(&config_file);
+ auto sonames = ReadConfig(config_file, always_true);
+ if (!sonames) {
+ LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
+ return "";
+ }
+ return android::base::Join(*sonames, ':');
+}
+
+static std::string InitNeuralNetworksPublicLibraries() {
+ return kNeuralNetworksApexPublicLibrary;
+}
+
+} // namespace
+
+const std::string& default_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries();
+ return list;
+}
+
+const std::string& runtime_public_libraries() {
+ static std::string list = InitRuntimePublicLibraries();
+ return list;
+}
+
+const std::string& vendor_public_libraries() {
+ static std::string list = InitVendorPublicLibraries();
+ return list;
+}
+
+const std::string& extended_public_libraries() {
+ static std::string list = InitExtendedPublicLibraries();
+ return list;
+}
+
+const std::string& neuralnetworks_public_libraries() {
+ static std::string list = InitNeuralNetworksPublicLibraries();
+ return list;
+}
+
+const std::string& llndk_libraries() {
+ static std::string list = InitLlndkLibraries();
+ return list;
+}
+
+const std::string& vndksp_libraries() {
+ static std::string list = InitVndkspLibraries();
+ return list;
+}
+
+} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
new file mode 100644
index 0000000..9bb3366
--- /dev/null
+++ b/libnativeloader/public_libraries.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <string>
+
+namespace android::nativeloader {
+
+// These provide the list of libraries that are available to the namespace for apps.
+// Not all of the libraries are available to apps. Depending on the context,
+// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& default_public_libraries();
+const std::string& runtime_public_libraries();
+const std::string& vendor_public_libraries();
+const std::string& extended_public_libraries();
+const std::string& neuralnetworks_public_libraries();
+const std::string& llndk_libraries();
+const std::string& vndksp_libraries();
+
+}; // namespace android::nativeloader
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index d528f30..4d5c53d 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -69,3 +69,14 @@
"libbase",
],
}
+
+// Build the test for the C API.
+cc_test {
+ name: "libnativeloader-api-tests",
+ host_supported: true,
+ test_per_src: true,
+ srcs: [
+ "api_test.c",
+ ],
+ header_libs: ["libnativeloader-headers"],
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/libnativeloader/test/api_test.c
similarity index 64%
copy from debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
copy to libnativeloader/test/api_test.c
index 5d0d924..e7025fd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
+++ b/libnativeloader/test/api_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-#ifndef _DEBUGGERD_ELF_UTILS_H
-#define _DEBUGGERD_ELF_UTILS_H
+/* The main purpose of this test is to ensure this C header compiles in C, so
+ * that no C++ features inadvertently leak into the C ABI. */
+#include "nativeloader/native_loader.h"
-#include <stdint.h>
-#include <string>
-
-namespace unwindstack {
-class Memory;
+int main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+ return 0;
}
-
-bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
-
-#endif // _DEBUGGERD_ELF_UTILS_H
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libnativeloader/utils.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to libnativeloader/utils.h
index 410d379..a1c2be5 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libnativeloader/utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include <abi_check/mkbootimg_abi_check.h>
+namespace android::nativeloader {
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#if defined(__LP64__)
+#define LIB "lib64"
+#else
+#define LIB "lib"
+#endif
+
+} // namespace android::nativeloader
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 1d43775..268496f 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -6,6 +6,7 @@
},
srcs: [
+ "checksum.c",
"dhcpclient.c",
"dhcpmsg.c",
"ifc_utils.c",
diff --git a/libnetutils/OWNERS b/libnetutils/OWNERS
index e3ec950..8321de6 100644
--- a/libnetutils/OWNERS
+++ b/libnetutils/OWNERS
@@ -1,3 +1,2 @@
-# TODO: should this be in system/netd?
-ek@google.com
-lorenzo@google.com
+include platform/system/netd:/OWNERS
+
diff --git a/libnetutils/checksum.c b/libnetutils/checksum.c
new file mode 100644
index 0000000..74b5fdd
--- /dev/null
+++ b/libnetutils/checksum.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * checksum.c - ipv4/ipv6 checksum calculation
+ */
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include "netutils/checksum.h"
+
+/* function: ip_checksum_add
+ * adds data to a checksum. only known to work on little-endian hosts
+ * current - the current checksum (or 0 to start a new checksum)
+ * data - the data to add to the checksum
+ * len - length of data
+ */
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len) {
+ uint32_t checksum = current;
+ int left = len;
+ const uint16_t* data_16 = data;
+
+ while (left > 1) {
+ checksum += *data_16;
+ data_16++;
+ left -= 2;
+ }
+ if (left) {
+ checksum += *(uint8_t*)data_16;
+ }
+
+ return checksum;
+}
+
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
+ * temp_sum - sum from ip_checksum_add
+ * returns: the folded checksum in network byte order
+ */
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
+ while (temp_sum > 0xffff) {
+ temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+ }
+ return temp_sum;
+}
+
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ * temp_sum - sum from ip_checksum_add
+ * returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+ return ~ip_checksum_fold(temp_sum);
+}
+
+/* function: ip_checksum
+ * combined ip_checksum_add and ip_checksum_finish
+ * data - data to checksum
+ * len - length of data
+ */
+uint16_t ip_checksum(const void* data, int len) {
+ // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
+ // is correctly calculated as 0.
+ uint32_t temp_sum;
+
+ temp_sum = ip_checksum_add(0, data, len);
+ return ip_checksum_finish(temp_sum);
+}
+
+/* function: ipv6_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp/icmp headers
+ * ip6 - the ipv6 header
+ * len - the transport length (transport header + payload)
+ * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
+ */
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
+ uint32_t checksum_len = htonl(len);
+ uint32_t checksum_next = htonl(protocol);
+
+ uint32_t current = 0;
+
+ current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
+ current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
+ current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
+ current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
+
+ return current;
+}
+
+/* function: ipv4_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp headers
+ * ip - the ipv4 header
+ * len - the transport length (transport header + payload)
+ */
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len) {
+ uint16_t temp_protocol, temp_length;
+
+ temp_protocol = htons(ip->protocol);
+ temp_length = htons(len);
+
+ uint32_t current = 0;
+
+ current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
+ current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
+ current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
+ current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
+
+ return current;
+}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ * checksum - the header checksum in the original packet in network byte order
+ * old_hdr_sum - the pseudo-header checksum of the original packet
+ * new_hdr_sum - the pseudo-header checksum of the translated packet
+ * returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+ // Algorithm suggested in RFC 1624.
+ // http://tools.ietf.org/html/rfc1624#section-3
+ checksum = ~checksum;
+ uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+ uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+ if (folded_sum > folded_old) {
+ return ~(folded_sum - folded_old);
+ } else {
+ return ~(folded_sum - folded_old - 1); // end-around borrow
+ }
+}
diff --git a/libnetutils/include/netutils/checksum.h b/libnetutils/include/netutils/checksum.h
new file mode 100644
index 0000000..868217c
--- /dev/null
+++ b/libnetutils/include/netutils/checksum.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <stdint.h>
+
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len);
+uint16_t ip_checksum_finish(uint32_t temp_sum);
+uint16_t ip_checksum(const void* data, int len);
+
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index d602c26..3cb6b9a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -53,6 +53,8 @@
char *seinfo;
gid_list gids;
void *private_data;
+ bool profileable_from_shell;
+ long version_code;
};
/**
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
index 3e1a3d1..edc533c 100644
--- a/libpackagelistparser/packagelistparser.c
+++ b/libpackagelistparser/packagelistparser.c
@@ -223,6 +223,32 @@
}
}
+ cur = strsep(&next, " \t\r\n");
+ if (cur) {
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"profileable_from_shell\" to integer value";
+ goto err;
+ }
+
+ /* should be a valid boolean of 1 or 0 */
+ if (!(tmp == 0 || tmp == 1)) {
+ errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value";
+ goto err;
+ }
+
+ pkg_info->profileable_from_shell = (bool)tmp;
+ }
+ cur = strsep(&next, " \t\r\n");
+ if (cur) {
+ tmp = strtoul(cur, &endptr, 10);
+ if (*endptr != '\0') {
+ errmsg = "Could not convert field \"versionCode\" to integer value";
+ goto err;
+ }
+ pkg_info->version_code = tmp;
+ }
+
rc = callback(pkg_info, userdata);
if (rc == false) {
/*
diff --git a/libpixelflinger/Android.bp b/libpixelflinger/Android.bp
new file mode 100644
index 0000000..76d9444
--- /dev/null
+++ b/libpixelflinger/Android.bp
@@ -0,0 +1,115 @@
+cc_defaults {
+ name: "pixelflinger_defaults",
+
+ cflags: [
+ "-fstrict-aliasing",
+ "-fomit-frame-pointer",
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-function",
+ ],
+ export_include_dirs: ["include"],
+ header_libs: ["libbase_headers"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+
+ arch: {
+ arm: {
+ neon: {
+ cflags: ["-D__ARM_HAVE_NEON"],
+ },
+ },
+ },
+}
+
+cc_library_static {
+ name: "libpixelflinger-arm",
+ defaults: ["pixelflinger_defaults"],
+
+ srcs: [
+ "fixed.cpp",
+ "picker.cpp",
+ "pixelflinger.cpp",
+ "trap.cpp",
+ "scanline.cpp",
+ ],
+
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
+
+// For the tests to use
+cc_library_headers {
+ name: "libpixelflinger_internal",
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+}
+
+cc_library {
+ name: "libpixelflinger",
+ defaults: ["pixelflinger_defaults"],
+
+ srcs: [
+ "codeflinger/ARMAssemblerInterface.cpp",
+ "codeflinger/ARMAssemblerProxy.cpp",
+ "codeflinger/CodeCache.cpp",
+ "codeflinger/GGLAssembler.cpp",
+ "codeflinger/load_store.cpp",
+ "codeflinger/blending.cpp",
+ "codeflinger/texturing.cpp",
+ "format.cpp",
+ "clear.cpp",
+ "raster.cpp",
+ "buffer.cpp",
+ ],
+ whole_static_libs: ["libpixelflinger-arm"],
+
+ arch: {
+ arm: {
+ srcs: [
+ "codeflinger/ARMAssembler.cpp",
+ "codeflinger/disassem.c",
+ "col32cb16blend.S",
+ "t32cb16blend.S",
+ ],
+
+ neon: {
+ srcs: ["col32cb16blend_neon.S"],
+ },
+ },
+ arm64: {
+ srcs: [
+ "codeflinger/Arm64Assembler.cpp",
+ "codeflinger/Arm64Disassembler.cpp",
+ "arch-arm64/col32cb16blend.S",
+ "arch-arm64/t32cb16blend.S",
+ ],
+ },
+ mips: {
+ mips32r6: {
+ srcs: [
+ "codeflinger/MIPSAssembler.cpp",
+ "codeflinger/mips_disassem.c",
+ "arch-mips/t32cb16blend.S",
+ ],
+ },
+ },
+ mips64: {
+ srcs: [
+ "codeflinger/MIPSAssembler.cpp",
+ "codeflinger/MIPS64Assembler.cpp",
+ "codeflinger/mips64_disassem.c",
+ "arch-mips64/col32cb16blend.S",
+ "arch-mips64/t32cb16blend.S",
+ ],
+ },
+ },
+}
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
deleted file mode 100644
index 8c80f6a..0000000
--- a/libpixelflinger/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-#
-# C/C++ and ARMv5 objects
-#
-
-include $(CLEAR_VARS)
-PIXELFLINGER_SRC_FILES:= \
- codeflinger/ARMAssemblerInterface.cpp \
- codeflinger/ARMAssemblerProxy.cpp \
- codeflinger/CodeCache.cpp \
- codeflinger/GGLAssembler.cpp \
- codeflinger/load_store.cpp \
- codeflinger/blending.cpp \
- codeflinger/texturing.cpp \
- fixed.cpp.arm \
- picker.cpp.arm \
- pixelflinger.cpp.arm \
- trap.cpp.arm \
- scanline.cpp.arm \
- format.cpp \
- clear.cpp \
- raster.cpp \
- buffer.cpp
-
-PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer
-PIXELFLINGER_CFLAGS += -Wall -Werror
-PIXELFLINGER_CFLAGS += -Wno-unused-function
-
-PIXELFLINGER_SRC_FILES_arm := \
- codeflinger/ARMAssembler.cpp \
- codeflinger/disassem.c \
- col32cb16blend.S \
- t32cb16blend.S \
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-PIXELFLINGER_SRC_FILES_arm += col32cb16blend_neon.S
-PIXELFLINGER_CFLAGS_arm += -D__ARM_HAVE_NEON
-endif
-
-PIXELFLINGER_SRC_FILES_arm64 := \
- codeflinger/Arm64Assembler.cpp \
- codeflinger/Arm64Disassembler.cpp \
- arch-arm64/col32cb16blend.S \
- arch-arm64/t32cb16blend.S \
-
-ifndef ARCH_MIPS_REV6
-PIXELFLINGER_SRC_FILES_mips := \
- codeflinger/MIPSAssembler.cpp \
- codeflinger/mips_disassem.c \
- arch-mips/t32cb16blend.S \
-
-endif
-
-PIXELFLINGER_SRC_FILES_mips64 := \
- codeflinger/MIPSAssembler.cpp \
- codeflinger/MIPS64Assembler.cpp \
- codeflinger/mips64_disassem.c \
- arch-mips64/col32cb16blend.S \
- arch-mips64/t32cb16blend.S \
-
-#
-# Shared library
-#
-
-LOCAL_MODULE:= libpixelflinger
-LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
-LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
-LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
-LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
-LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
-LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-LOCAL_HEADER_LIBRARIES := libbase_headers
-LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp
index 8516640..32691a3 100644
--- a/libpixelflinger/codeflinger/CodeCache.cpp
+++ b/libpixelflinger/codeflinger/CodeCache.cpp
@@ -61,7 +61,11 @@
#define USAGE_ERROR_ACTION(m,p) \
heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
#include "../../../../external/dlmalloc/malloc.c"
+#pragma GCC diagnostic pop
static void heap_error(const char* msg, const char* function, void* p) {
ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p",
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 7f39e9b..4217a89 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -107,7 +107,7 @@
// inline ARM implementations
inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) CONST;
-inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulx(GGLfixed x, GGLfixed y, int shift) {
GGLfixed result, t;
if (__builtin_constant_p(shift)) {
asm("smull %[lo], %[hi], %[x], %[y] \n"
@@ -130,7 +130,8 @@
}
inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) CONST;
-inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a, int shift) {
+__attribute__((always_inline)) inline GGLfixed gglMulAddx(GGLfixed x, GGLfixed y, GGLfixed a,
+ int shift) {
GGLfixed result, t;
if (__builtin_constant_p(shift)) {
asm("smull %[lo], %[hi], %[x], %[y] \n"
diff --git a/libpixelflinger/tests/Android.bp b/libpixelflinger/tests/Android.bp
new file mode 100644
index 0000000..e20dd93
--- /dev/null
+++ b/libpixelflinger/tests/Android.bp
@@ -0,0 +1,17 @@
+cc_defaults {
+ name: "pixelflinger-tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: ["libpixelflinger_internal"],
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libpixelflinger",
+ "libutils",
+ ],
+}
diff --git a/libpixelflinger/tests/Android.mk b/libpixelflinger/tests/Android.mk
deleted file mode 100644
index 6571161..0000000
--- a/libpixelflinger/tests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(all-subdir-makefiles)
diff --git a/libpixelflinger/tests/arch-arm64/Android.bp b/libpixelflinger/tests/arch-arm64/Android.bp
new file mode 100644
index 0000000..2f5586a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-arm64",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ arm64: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-arm64/Android.mk b/libpixelflinger/tests/arch-arm64/Android.mk
deleted file mode 100644
index ca58b4b..0000000
--- a/libpixelflinger/tests/arch-arm64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),arm64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.bp b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
new file mode 100644
index 0000000..003f485
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "test-pixelflinger-arm64-assembler-test",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: [
+ "arm64_assembler_test.cpp",
+ "asm_test_jacket.S",
+ ],
+}
diff --git a/libpixelflinger/tests/arch-arm64/assembler/Android.mk b/libpixelflinger/tests/arch-arm64/assembler/Android.mk
deleted file mode 100644
index db5dc4d..0000000
--- a/libpixelflinger/tests/arch-arm64/assembler/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- arm64_assembler_test.cpp\
- asm_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-arm64-assembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..e640aeb
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-col32cb16blend",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
deleted file mode 100644
index 3096232..0000000
--- a/libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-arm64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-col32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.bp b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
new file mode 100644
index 0000000..38dc99a
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-disassembler-test",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["arm64_diassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk b/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
deleted file mode 100644
index 78f12af..0000000
--- a/libpixelflinger/tests/arch-arm64/disassembler/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- arm64_diassembler_test.cpp \
- ../../../codeflinger/Arm64Disassembler.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-disassembler-test
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
new file mode 100644
index 0000000..9d060d1
--- /dev/null
+++ b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-arm64-t32cb16blend",
+ defaults: ["pixelflinger-tests-arm64"],
+
+ srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
deleted file mode 100644
index 664347f..0000000
--- a/libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- t32cb16blend_test.c \
- ../../../arch-arm64/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-arm64-t32cb16blend
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/Android.bp b/libpixelflinger/tests/arch-mips/Android.bp
new file mode 100644
index 0000000..2ca2721
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-mips",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ mips: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
deleted file mode 100644
index fe6979e..0000000
--- a/libpixelflinger/tests/arch-mips/Android.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-ifeq ($(TARGET_ARCH),mips)
-include $(all-subdir-makefiles)
-endif
-ifeq ($(TARGET_ARCH),mipsel)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
new file mode 100644
index 0000000..45bfe29
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips-col32cb16blend",
+ defaults: ["pixelflinger-tests-mips"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
deleted file mode 100644
index 40f197f..0000000
--- a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-mips/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
new file mode 100644
index 0000000..069e97c
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips-t32cb16blend",
+ defaults: ["pixelflinger-tests-mips"],
+
+ srcs: ["t32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
deleted file mode 100644
index d0c0ae4..0000000
--- a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- t32cb16blend_test.c \
- ../../../arch-mips/t32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 32
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/Android.bp b/libpixelflinger/tests/arch-mips64/Android.bp
new file mode 100644
index 0000000..ba55d62
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.bp
@@ -0,0 +1,11 @@
+cc_defaults {
+ name: "pixelflinger-tests-mips64",
+ defaults: ["pixelflinger-tests"],
+
+ enabled: false,
+ arch: {
+ mips64: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
deleted file mode 100644
index 3b1c64e..0000000
--- a/libpixelflinger/tests/arch-mips64/Android.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-ifeq ($(TARGET_ARCH),mips64)
-include $(all-subdir-makefiles)
-endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.bp b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
new file mode 100644
index 0000000..b672053
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.bp
@@ -0,0 +1,9 @@
+cc_test {
+ name: "test-pixelflinger-mips64-assembler-test",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: [
+ "mips64_assembler_test.cpp",
+ "asm_mips_test_jacket.S",
+ ],
+}
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
deleted file mode 100644
index 4699961..0000000
--- a/libpixelflinger/tests/arch-mips64/assembler/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- mips64_assembler_test.cpp\
- asm_mips_test_jacket.S
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../..
-
-LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
new file mode 100644
index 0000000..bfc6ae9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips64-col32cb16blend",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: ["col32cb16blend_test.c"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
deleted file mode 100644
index 7d4177e..0000000
--- a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- col32cb16blend_test.c \
- ../../../arch-mips64/col32cb16blend.S
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.bp b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
new file mode 100644
index 0000000..96bf9e9
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.bp
@@ -0,0 +1,6 @@
+cc_test {
+ name: "test-pixelflinger-mips64-disassembler-test",
+ defaults: ["pixelflinger-tests-mips64"],
+
+ srcs: ["mips64_disassembler_test.cpp"],
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
deleted file mode 100644
index 4e72b57..0000000
--- a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- mips64_disassembler_test.cpp \
- ../../../codeflinger/mips64_disassem.c
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MULTILIB := 64
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/codegen/Android.bp b/libpixelflinger/tests/codegen/Android.bp
new file mode 100644
index 0000000..7e4bcfb
--- /dev/null
+++ b/libpixelflinger/tests/codegen/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+ name: "test-opengl-codegen",
+ defaults: ["pixelflinger-tests"],
+
+ srcs: ["codegen.cpp"],
+
+ arch: {
+ arm: {
+ instruction_set: "arm",
+ },
+ },
+}
diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk
deleted file mode 100644
index 72d71ef..0000000
--- a/libpixelflinger/tests/codegen/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- codegen.cpp.arm
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libpixelflinger
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../..
-
-LOCAL_MODULE:= test-opengl-codegen
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/gglmul/Android.bp b/libpixelflinger/tests/gglmul/Android.bp
new file mode 100644
index 0000000..288337b
--- /dev/null
+++ b/libpixelflinger/tests/gglmul/Android.bp
@@ -0,0 +1,12 @@
+cc_test {
+ name: "test-pixelflinger-gglmul",
+
+ srcs: ["gglmul_test.cpp"],
+
+ header_libs: ["libpixelflinger_internal"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libpixelflinger/tests/gglmul/Android.mk b/libpixelflinger/tests/gglmul/Android.mk
deleted file mode 100644
index 67f358f..0000000
--- a/libpixelflinger/tests/gglmul/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- gglmul_test.cpp
-
-LOCAL_SHARED_LIBRARIES :=
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../include
-
-LOCAL_MODULE:= test-pixelflinger-gglmul
-
-LOCAL_CFLAGS:= -Wall -Werror
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c38279d..618a5c5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,12 +1,55 @@
+cc_library_headers {
+ name: "libprocessgroup_headers",
+ vendor_available: true,
+ recovery_available: true,
+ host_supported: true,
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library {
- srcs: ["processgroup.cpp"],
+ srcs: [
+ "cgroup_map.cpp",
+ "processgroup.cpp",
+ "sched_policy.cpp",
+ "task_profiles.cpp",
+ ],
name: "libprocessgroup",
host_supported: true,
+ native_bridge_supported: true,
recovery_available: true,
- shared_libs: ["libbase"],
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ ],
+ static_libs: [
+ "libjsoncpp",
+ ],
+ // for cutils/android_filesystem_config.h
+ header_libs: [
+ "libcutils_headers",
+ "libprocessgroup_headers",
+ ],
export_include_dirs: ["include"],
+ export_header_lib_headers: [
+ "libprocessgroup_headers",
+ ],
cflags: [
"-Wall",
"-Werror",
+ "-Wexit-time-destructors",
],
}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
new file mode 100644
index 0000000..20ae2be
--- /dev/null
+++ b/libprocessgroup/cgroup_map.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <cgroup_map.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/processgroup.h>
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
+static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
+static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
+
+uint32_t CgroupController::version() const {
+ CHECK(HasValue());
+ return ACgroupController_getVersion(controller_);
+}
+
+const char* CgroupController::name() const {
+ CHECK(HasValue());
+ return ACgroupController_getName(controller_);
+}
+
+const char* CgroupController::path() const {
+ CHECK(HasValue());
+ return ACgroupController_getPath(controller_);
+}
+
+bool CgroupController::HasValue() const {
+ return controller_ != nullptr;
+}
+
+bool CgroupController::IsUsable() {
+ if (!HasValue()) return false;
+
+ if (state_ == UNKNOWN) {
+ state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
+ }
+
+ return state_ == USABLE;
+}
+
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+ std::string tasks_path = path();
+
+ if (!rel_path.empty()) {
+ tasks_path += "/" + rel_path;
+ }
+ return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
+}
+
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
+ pid_t pid) const {
+ std::string proc_path(path());
+ proc_path.append("/").append(rel_path);
+ proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
+ proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
+
+ return proc_path.append(CGROUP_PROCS_FILE);
+}
+
+bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
+ std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
+ std::string content;
+ if (!android::base::ReadFileToString(file_name, &content)) {
+ PLOG(ERROR) << "Failed to read " << file_name;
+ return false;
+ }
+
+ // if group is null and tid exists return early because
+ // user is not interested in cgroup membership
+ if (group == nullptr) {
+ return true;
+ }
+
+ std::string cg_tag = StringPrintf(":%s:", name());
+ size_t start_pos = content.find(cg_tag);
+ if (start_pos == std::string::npos) {
+ return false;
+ }
+
+ start_pos += cg_tag.length() + 1; // skip '/'
+ size_t end_pos = content.find('\n', start_pos);
+ if (end_pos == std::string::npos) {
+ *group = content.substr(start_pos, std::string::npos);
+ } else {
+ *group = content.substr(start_pos, end_pos - start_pos);
+ }
+
+ return true;
+}
+
+CgroupMap::CgroupMap() {
+ if (!LoadRcFile()) {
+ LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
+ }
+}
+
+CgroupMap& CgroupMap::GetInstance() {
+ // Deliberately leak this object to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* instance = new CgroupMap;
+ return *instance;
+}
+
+bool CgroupMap::LoadRcFile() {
+ if (!loaded_) {
+ loaded_ = (ACgroupFile_getVersion() != 0);
+ }
+ return loaded_;
+}
+
+void CgroupMap::Print() const {
+ if (!loaded_) {
+ LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return;
+ }
+ LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+ LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
+
+ LOG(INFO) << "Mounted cgroups:";
+
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+ << ACgroupController_getVersion(controller) << " path "
+ << ACgroupController_getPath(controller);
+ }
+}
+
+CgroupController CgroupMap::FindController(const std::string& name) const {
+ if (!loaded_) {
+ LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
+ << "] failed, RC file was not initialized properly";
+ return CgroupController(nullptr);
+ }
+
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ if (name == ACgroupController_getName(controller)) {
+ return CgroupController(controller);
+ }
+ }
+
+ return CgroupController(nullptr);
+}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
new file mode 100644
index 0000000..427d71b
--- /dev/null
+++ b/libprocessgroup/cgroup_map.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
+class CgroupController {
+ public:
+ // Does not own controller
+ explicit CgroupController(const ACgroupController* controller)
+ : controller_(controller), state_(UNKNOWN) {}
+
+ uint32_t version() const;
+ const char* name() const;
+ const char* path() const;
+
+ bool HasValue() const;
+ bool IsUsable();
+
+ std::string GetTasksFilePath(const std::string& path) const;
+ std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
+ bool GetTaskGroup(int tid, std::string* group) const;
+ private:
+ enum ControllerState {
+ UNKNOWN = 0,
+ USABLE = 1,
+ MISSING = 2,
+ };
+
+ const ACgroupController* controller_ = nullptr;
+ ControllerState state_;
+};
+
+class CgroupMap {
+ public:
+ // Selinux policy ensures only init process can successfully use this function
+ static bool SetupCgroups();
+
+ static CgroupMap& GetInstance();
+ CgroupController FindController(const std::string& name) const;
+
+ private:
+ bool loaded_ = false;
+ CgroupMap();
+ bool LoadRcFile();
+ void Print() const;
+};
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..0af75bb
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,63 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libcgrouprc",
+ host_supported: true,
+ recovery_available: true,
+ // Do not ever mark this as vendor_available; otherwise, vendor modules
+ // that links to the static library will behave unexpectedly. All on-device
+ // modules should use libprocessgroup which links to the LL-NDK library
+ // defined below. The static library is built for tests.
+ vendor_available: false,
+ native_bridge_supported: true,
+ srcs: [
+ "cgroup_controller.cpp",
+ "cgroup_file.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ stubs: {
+ symbol_file: "libcgrouprc.llndk.txt",
+ versions: ["29"],
+ },
+ target: {
+ linux: {
+ version_script: "libcgrouprc.llndk.txt",
+ },
+ },
+}
+
+llndk_library {
+ name: "libcgrouprc",
+ symbol_file: "libcgrouprc.llndk.txt",
+ native_bridge_supported: true,
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+ struct stat sb;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ size_t file_size = sb.st_size;
+ if (file_size < sizeof(CgroupFile)) {
+ LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (file_data == MAP_FAILED) {
+ PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+ if (file_size != expected) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+ << ", actual " << file_size;
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ return file_data;
+}
+
+static CgroupFile* GetInstance() {
+ // Deliberately leak this object (not munmap) to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* file = LoadRcFile();
+ return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+ auto file = GetInstance();
+ if (file == nullptr) return nullptr;
+ CHECK(index < file->controller_count_);
+ // Although the object is not actually an ACgroupController object, all ACgroupController_*
+ // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+ // member functions.
+ return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libprocessgroup/cgrouprc/cgrouprc_internal.h
similarity index 67%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to libprocessgroup/cgrouprc/cgrouprc_internal.h
index 410d379..cd02f03 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+#include <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..0f6a9cd
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+ uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Flag bitmask to be used when ACgroupController_getFlags can be exported
+ */
+#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.llndk.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+ global:
+ ACgroupFile_getVersion;
+ ACgroupFile_getControllerCount;
+ ACgroupFile_getController;
+ ACgroupController_getVersion;
+ ACgroupController_getName;
+ ACgroupController_getPath;
+ local:
+ *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..559a869
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libcgrouprc_format",
+ host_supported: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ srcs: [
+ "cgroup_controller.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..202b23e
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController() : version_(0), flags_(0) {
+ memset(name_, 0, sizeof(name_));
+ memset(path_, 0, sizeof(path_));
+}
+
+CgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name,
+ const std::string& path)
+ : CgroupController() {
+ // strlcpy isn't available on host. Although there is an implementation
+ // in licutils, libcutils itself depends on libcgrouprc_format, causing
+ // a circular dependency.
+ version_ = version;
+ flags_ = flags;
+ strncpy(name_, name.c_str(), sizeof(name_) - 1);
+ name_[sizeof(name_) - 1] = '\0';
+ strncpy(path_, path.c_str(), sizeof(path_) - 1);
+ path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+ return version_;
+}
+
+uint32_t CgroupController::flags() const {
+ return flags_;
+}
+
+const char* CgroupController::name() const {
+ return name_;
+}
+
+const char* CgroupController::path() const {
+ return path_;
+}
+
+void CgroupController::set_flags(uint32_t flags) {
+ flags_ = flags;
+}
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..40d8548
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+ public:
+ CgroupController();
+ CgroupController(uint32_t version, uint32_t flags, const std::string& name,
+ const std::string& path);
+
+ uint32_t version() const;
+ uint32_t flags() const;
+ const char* name() const;
+ const char* path() const;
+
+ void set_flags(uint32_t flags);
+
+ private:
+ static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+ static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+ uint32_t version_;
+ uint32_t flags_;
+ char name_[CGROUP_NAME_BUF_SZ];
+ char path_[CGROUP_PATH_BUF_SZ];
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+ uint32_t version_;
+ uint32_t controller_count_;
+ CgroupController controllers_[];
+
+ static constexpr uint32_t FILE_VERSION_1 = 1;
+ static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 9fa4154..f73ec2d 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -14,14 +14,36 @@
* limitations under the License.
*/
-#ifndef _PROCESSGROUP_H_
-#define _PROCESSGROUP_H_
+#pragma once
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string>
+#include <vector>
__BEGIN_DECLS
+static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+
+bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
+bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
+bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
+
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+ bool use_fd_cache = false);
+
+#ifndef __ANDROID_VNDK__
+
+static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
+
+bool UsePerAppMemcg();
+
+// Drop the fd cache of cgroup path. It is used for when resource caching is enabled and a process
+// loses the access to the path, the access checking (See SetCgroupAction::EnableResourceCaching)
+// should be active again. E.g. Zygote specialization for child process.
+void DropTaskProfilesResourceCaching();
+
// Return 0 and removes the cgroup if there are no longer any processes in it.
// Returns -1 in the case of an error occurring or if there are processes still running
// even after retrying for up to 200ms.
@@ -31,14 +53,16 @@
// that it only returns 0 in the case that the cgroup exists and it contains no processes.
int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
-int createProcessGroup(uid_t uid, int initialPid);
+int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
+// Set various properties of a process group. For these functions to work, the process group must
+// have been created by passing memControl=true to createProcessGroup.
bool setProcessGroupSwappiness(uid_t uid, int initialPid, int swappiness);
bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
void removeAllProcessGroups(void);
-__END_DECLS
+#endif // __ANDROID_VNDK__
-#endif
+__END_DECLS
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
new file mode 100644
index 0000000..3c498da
--- /dev/null
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Check if Linux kernel enables CPUSETS feature.
+ *
+ * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.
+ */
+extern bool cpusets_enabled();
+
+/*
+ * Check if Linux kernel enables SCHEDTUNE feature (only available in Android
+ * common kernel or Linaro LSK, not in mainline Linux as of v4.9)
+ *
+ * Return value: 1 if Linux kernel CONFIG_CGROUP_SCHEDTUNE=y; 0 otherwise.
+ */
+extern bool schedboost_enabled();
+
+/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */
+typedef enum {
+ SP_DEFAULT = -1,
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+ SP_SYSTEM = 2, // can't be used with set_sched_policy()
+ SP_AUDIO_APP = 3,
+ SP_AUDIO_SYS = 4,
+ SP_TOP_APP = 5,
+ SP_RT_APP = 6,
+ SP_RESTRICTED = 7,
+ SP_CNT,
+ SP_MAX = SP_CNT - 1,
+ SP_SYSTEM_DEFAULT = SP_FOREGROUND,
+} SchedPolicy;
+
+extern int set_cpuset_policy(int tid, SchedPolicy policy);
+
+/* Assign thread tid to the cgroup associated with the specified policy.
+ * If the thread is a thread group leader, that is it's gettid() == getpid(),
+ * then the other threads in the same thread group are _not_ affected.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -errno for error.
+ */
+extern int set_sched_policy(int tid, SchedPolicy policy);
+
+/* Return the policy associated with the cgroup of thread tid via policy pointer.
+ * On platforms which support gettid(), zero tid means current thread.
+ * Return value: 0 for success, or -1 for error and set errno.
+ */
+extern int get_sched_policy(int tid, SchedPolicy* policy);
+
+/* Return a displayable string corresponding to policy.
+ * Return value: non-NULL NUL-terminated name of unspecified length;
+ * the caller is responsible for displaying the useful part of the string.
+ */
+extern const char* get_sched_policy_name(SchedPolicy policy);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 0a42053..7c191be 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -25,12 +25,12 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
+#include <map>
#include <memory>
#include <mutex>
#include <set>
@@ -42,9 +42,9 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
-
+#include <cutils/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+#include <task_profiles.h>
using android::base::GetBoolProperty;
using android::base::StartsWith;
@@ -53,56 +53,132 @@
using namespace std::chrono_literals;
-#define MEM_CGROUP_PATH "/dev/memcg/apps"
-#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
-#define ACCT_CGROUP_PATH "/acct"
-
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-std::once_flag init_path_flag;
+bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
+ auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
-static const std::string& GetCgroupRootPath() {
- static std::string cgroup_root_path;
- std::call_once(init_path_flag, [&]() {
- // low-ram devices use per-app memcg by default, unlike high-end ones
- bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
- bool per_app_memcg =
- GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
- if (per_app_memcg) {
- // Check if mem cgroup is mounted, only then check for
- // write-access to avoid SELinux denials
- cgroup_root_path =
- (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
- ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+ if (!controller.HasValue()) {
+ return false;
+ }
+
+ if (path) {
+ *path = controller.path();
+ }
+
+ return true;
+}
+
+bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+ const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+ if (attr == nullptr) {
+ return false;
+ }
+
+ if (path) {
+ *path = StringPrintf("%s/%s", attr->controller()->path(), attr->file_name().c_str());
+ }
+
+ return true;
+}
+
+bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+ const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+
+ if (attr == nullptr) {
+ return false;
+ }
+
+ if (!attr->GetPathForTask(tid, path)) {
+ PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return false;
+ }
+
+ return true;
+}
+
+bool UsePerAppMemcg() {
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ return GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+}
+
+static bool isMemoryCgroupSupported() {
+ static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").IsUsable();
+
+ return memcg_supported;
+}
+
+void DropTaskProfilesResourceCaching() {
+ TaskProfiles::GetInstance().DropResourceCaching();
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+ bool use_fd_cache) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+ for (const auto& name : profiles) {
+ TaskProfile* profile = tp.GetProfile(name);
+ if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching();
+ }
+ if (!profile->ExecuteForProcess(uid, pid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ }
} else {
- cgroup_root_path = ACCT_CGROUP_PATH;
+ PLOG(WARNING) << "Failed to find " << name << "process profile";
}
- });
- return cgroup_root_path;
+ }
+
+ return true;
}
-static std::string ConvertUidToPath(uid_t uid) {
- return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
+bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
+ const TaskProfiles& tp = TaskProfiles::GetInstance();
+
+ for (const auto& name : profiles) {
+ TaskProfile* profile = tp.GetProfile(name);
+ if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching();
+ }
+ if (!profile->ExecuteForTask(tid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << "task profile";
+ }
+ }
+
+ return true;
}
-static std::string ConvertUidPidToPath(uid_t uid, int pid) {
- return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+ return StringPrintf("%s/uid_%d", cgroup, uid);
}
-static int RemoveProcessGroup(uid_t uid, int pid) {
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+ return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
+}
+
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
int ret;
- auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+ auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
ret = rmdir(uid_pid_path.c_str());
- auto uid_path = ConvertUidToPath(uid);
+ auto uid_path = ConvertUidToPath(cgroup, uid);
rmdir(uid_path.c_str());
return ret;
}
-static void RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
+ bool empty = true;
if (uid != NULL) {
dirent* dir;
while ((dir = readdir(uid.get())) != nullptr) {
@@ -116,44 +192,85 @@
auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1) {
+ if (errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
+ empty = false;
+ }
+ }
+ }
+ return empty;
+}
+
+void removeAllProcessGroups() {
+ LOG(VERBOSE) << "removeAllProcessGroups()";
+
+ std::vector<std::string> cgroups;
+ std::string path;
+
+ if (CgroupGetControllerPath("cpuacct", &path)) {
+ cgroups.push_back(path);
+ }
+ if (CgroupGetControllerPath("memory", &path)) {
+ cgroups.push_back(path + "/apps");
+ }
+
+ for (std::string cgroup_root_path : cgroups) {
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
+ if (root == NULL) {
+ PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ } else {
+ dirent* dir;
+ while ((dir = readdir(root.get())) != nullptr) {
+ if (dir->d_type != DT_DIR) {
+ continue;
+ }
+
+ if (!StartsWith(dir->d_name, "uid_")) {
+ continue;
+ }
+
+ auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
+ if (!RemoveUidProcessGroups(path)) {
+ LOG(VERBOSE) << "Skip removing " << path;
+ continue;
+ }
+ LOG(VERBOSE) << "Removing " << path;
+ if (rmdir(path.c_str()) == -1 && errno != EBUSY) {
+ PLOG(WARNING) << "Failed to remove " << path;
+ }
+ }
}
}
}
-void removeAllProcessGroups()
-{
- LOG(VERBOSE) << "removeAllProcessGroups()";
- const auto& cgroup_root_path = GetCgroupRootPath();
- std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
- if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
- } else {
- dirent* dir;
- while ((dir = readdir(root.get())) != nullptr) {
- if (dir->d_type != DT_DIR) {
- continue;
- }
-
- if (!StartsWith(dir->d_name, "uid_")) {
- continue;
- }
-
- auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
- RemoveUidProcessGroups(path);
- LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
- }
+static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
+ return false;
}
+
+ if (chown(path.c_str(), uid, gid) == -1) {
+ int saved_errno = errno;
+ rmdir(path.c_str());
+ errno = saved_errno;
+ return false;
+ }
+
+ return true;
}
// Returns number of processes killed on success
// Returns 0 if there are no processes in the process cgroup left to kill
// Returns -1 on error
-static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
+ auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
if (!fd) {
+ if (errno == ENOENT) {
+ // This happens when process is already dead
+ return 0;
+ }
PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
return -1;
}
@@ -198,7 +315,7 @@
LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
<< " as part of process cgroup " << initialPid;
- if (kill(-pgid, signal) == -1) {
+ if (kill(-pgid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed";
}
}
@@ -208,7 +325,7 @@
LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup "
<< initialPid;
- if (kill(pid, signal) == -1) {
+ if (kill(pid, signal) == -1 && errno != ESRCH) {
PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed";
}
}
@@ -217,11 +334,23 @@
}
static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+ std::string cpuacct_path;
+ std::string memory_path;
+
+ CgroupGetControllerPath("cpuacct", &cpuacct_path);
+ CgroupGetControllerPath("memory", &memory_path);
+ memory_path += "/apps";
+
+ const char* cgroup =
+ (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
+ ? cpuacct_path.c_str()
+ : memory_path.c_str();
+
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
int retry = retries;
int processes;
- while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -251,7 +380,7 @@
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
- return RemoveProcessGroup(uid, initialPid);
+ return RemoveProcessGroup(cgroup, uid, initialPid);
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -270,31 +399,23 @@
return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
-static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
- if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
- return false;
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+ std::string cgroup;
+ if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
+ CgroupGetControllerPath("memory", &cgroup);
+ cgroup += "/apps";
+ } else {
+ CgroupGetControllerPath("cpuacct", &cgroup);
}
- if (chown(path.c_str(), uid, gid) == -1) {
- int saved_errno = errno;
- rmdir(path.c_str());
- errno = saved_errno;
- return false;
- }
-
- return true;
-}
-
-int createProcessGroup(uid_t uid, int initialPid)
-{
- auto uid_path = ConvertUidToPath(uid);
+ auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
- auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
+ auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
@@ -312,13 +433,17 @@
return ret;
}
-static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
- if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
+ if (!isMemoryCgroupSupported()) {
PLOG(ERROR) << "Memcg is not mounted.";
return false;
}
- auto path = ConvertUidPidToPath(uid, pid) + file_name;
+ std::string path;
+ if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
+ PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+ return false;
+ }
if (!WriteStringToFile(std::to_string(value), path)) {
PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -327,14 +452,14 @@
return true;
}
-bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
- return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+bool setProcessGroupSwappiness(uid_t, int pid, int swappiness) {
+ return SetProcessGroupValue(pid, "MemSwappiness", swappiness);
}
-bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
- return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+bool setProcessGroupSoftLimit(uid_t, int pid, int64_t soft_limit_in_bytes) {
+ return SetProcessGroupValue(pid, "MemSoftLimit", soft_limit_in_bytes);
}
-bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
- return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
+ return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
new file mode 100644
index 0000000..e05a690
--- /dev/null
+++ b/libprocessgroup/profiles/Android.bp
@@ -0,0 +1,108 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+prebuilt_etc {
+ name: "cgroups.json",
+ src: "cgroups.json",
+}
+
+prebuilt_etc {
+ name: "cgroups.recovery.json",
+ filename: "cgroups.json",
+ recovery: true,
+ src: "cgroups.recovery.json",
+}
+
+prebuilt_etc {
+ name: "task_profiles.json",
+ src: "task_profiles.json",
+}
+
+cc_defaults {
+ name: "libprocessgroup_test_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+
+ // Needed for headers from libprotobuf.
+ "-Wno-unused-parameter",
+ ],
+}
+
+cc_library_static {
+ name: "libprocessgroup_proto",
+ host_supported: true,
+ defaults: ["libprocessgroup_test_defaults"],
+ srcs: [
+ "cgroups.proto",
+ "task_profiles.proto",
+ ],
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ },
+}
+
+cc_test_host {
+ name: "libprocessgroup_proto_test",
+ defaults: ["libprocessgroup_test_defaults"],
+ srcs: [
+ "test.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "liblog",
+ "libjsoncpp",
+ "libjsonpbverify",
+ "libjsonpbparse",
+ "libprocessgroup_proto",
+ ],
+ shared_libs: [
+ "libprotobuf-cpp-full",
+ ],
+ data: [
+ "cgroups.json",
+ "cgroups.recovery.json",
+ "task_profiles.json",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
+
+cc_test {
+ name: "vts_processgroup_validate_test",
+ defaults: ["libprocessgroup_test_defaults"],
+ srcs: [
+ "test_vendor.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "libjsonpbverify",
+ "libjsonpbparse",
+ "libprocessgroup_proto",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libjsoncpp",
+ "libprotobuf-cpp-full",
+ ],
+ target: {
+ android: {
+ test_config: "vts_processgroup_validate_test.xml",
+ },
+ },
+}
diff --git a/Android.mk b/libprocessgroup/profiles/Android.mk
similarity index 72%
copy from Android.mk
copy to libprocessgroup/profiles/Android.mk
index 7c57258..eab96d4 100644
--- a/Android.mk
+++ b/libprocessgroup/profiles/Android.mk
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-LOCAL_PATH := $(my-dir)
-include $(call first-makefiles-under,$(LOCAL_PATH))
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := VtsProcessgroupValidateTest
+-include test/vts/tools/build/Android.host_config.mk
diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING
new file mode 100644
index 0000000..5ff4112
--- /dev/null
+++ b/libprocessgroup/profiles/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "libprocessgroup_proto_test",
+ "host": true
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
new file mode 100644
index 0000000..5871a63
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.json
@@ -0,0 +1,50 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "blkio",
+ "Path": "/dev/blkio",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpu",
+ "Path": "/dev/cpuctl",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ },
+ {
+ "Controller": "cpuset",
+ "Path": "/dev/cpuset",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ },
+ {
+ "Controller": "memory",
+ "Path": "/dev/memcg",
+ "Mode": "0700",
+ "UID": "root",
+ "GID": "system"
+ },
+ {
+ "Controller": "schedtune",
+ "Path": "/dev/stune",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ],
+ "Cgroups2": {
+ "Path": "/dev/cg2_bpf",
+ "Mode": "0600",
+ "UID": "root",
+ "GID": "root"
+ }
+}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
new file mode 100644
index 0000000..f4070c5
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message Cgroups {
+ repeated Cgroup cgroups = 1 [json_name = "Cgroups"];
+ Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
+}
+
+// Next: 6
+message Cgroup {
+ string controller = 1 [json_name = "Controller"];
+ string path = 2 [json_name = "Path"];
+ string mode = 3 [json_name = "Mode"];
+ string uid = 4 [json_name = "UID"];
+ string gid = 5 [json_name = "GID"];
+}
+
+// Next: 5
+message Cgroups2 {
+ string path = 1 [json_name = "Path"];
+ string mode = 2 [json_name = "Mode"];
+ string uid = 3 [json_name = "UID"];
+ string gid = 4 [json_name = "GID"];
+}
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
new file mode 100644
index 0000000..f0bf5fd
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -0,0 +1,9 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/cgroups_test.h b/libprocessgroup/profiles/cgroups_test.h
new file mode 100644
index 0000000..1309957
--- /dev/null
+++ b/libprocessgroup/profiles/cgroups_test.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups.pb.h"
+
+using ::testing::MatchesRegex;
+
+namespace android {
+namespace profiles {
+
+class CgroupsTest : public jsonpb::JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ cgroups_ = static_cast<Cgroups*>(message());
+ }
+ Cgroups* cgroups_;
+};
+
+TEST_P(CgroupsTest, CgroupRequiredFields) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ auto&& cgroup = cgroups_->cgroups(i);
+ EXPECT_FALSE(cgroup.controller().empty())
+ << "No controller name for cgroup #" << i << " in " << file_path_;
+ EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2RequiredFields) {
+ if (cgroups_->has_cgroups2()) {
+ EXPECT_FALSE(cgroups_->cgroups2().path().empty())
+ << "No path for cgroup2 in " << file_path_;
+ }
+}
+
+// "Mode" field must be in the format of "0xxx".
+static inline constexpr const char* REGEX_MODE = "(0[0-7]{3})?";
+TEST_P(CgroupsTest, CgroupMode) {
+ for (int i = 0; i < cgroups_->cgroups_size(); ++i) {
+ EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroup controller #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(CgroupsTest, Cgroup2Mode) {
+ EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))
+ << "For cgroups2 in " << file_path_;
+}
+
+} // namespace profiles
+} // namespace android
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
new file mode 100644
index 0000000..74a39cd
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -0,0 +1,498 @@
+{
+ "Attributes": [
+ {
+ "Name": "LowCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "background/cpus"
+ },
+ {
+ "Name": "HighCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "foreground/cpus"
+ },
+ {
+ "Name": "MaxCapacityCPUs",
+ "Controller": "cpuset",
+ "File": "top-app/cpus"
+ },
+
+ {
+ "Name": "MemLimit",
+ "Controller": "memory",
+ "File": "memory.limit_in_bytes"
+ },
+ {
+ "Name": "MemSoftLimit",
+ "Controller": "memory",
+ "File": "memory.soft_limit_in_bytes"
+ },
+ {
+ "Name": "MemSwappiness",
+ "Controller": "memory",
+ "File": "memory.swappiness"
+ },
+ {
+ "Name": "STuneBoost",
+ "Controller": "schedtune",
+ "File": "schedtune.boost"
+ },
+ {
+ "Name": "STunePreferIdle",
+ "Controller": "schedtune",
+ "File": "schedtune.prefer_idle"
+ },
+ {
+ "Name": "UClampMin",
+ "Controller": "cpu",
+ "File": "cpu.util.min"
+ },
+ {
+ "Name": "UClampMax",
+ "Controller": "cpu",
+ "File": "cpu.util.max"
+ }
+ ],
+
+ "Profiles": [
+ {
+ "Name": "HighEnergySaving",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "RealtimePerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "schedtune",
+ "Path": "rt"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CpuPolicySpread",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "1"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "CpuPolicyPack",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "STunePreferIdle",
+ "Value": "0"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "VrKernelCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrServiceCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system/performance"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "VrProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "application/performance"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ProcessCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityNormal",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityHigh",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "foreground"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ProcessCapacityMax",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "top-app"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "ServiceCapacityLow",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "system-background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "ServiceCapacityRestricted",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "restricted"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "CameraServiceCapacity",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpuset",
+ "Path": "camera-daemon"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": "background"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "NormalIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+ {
+ "Name": "MaxIoPriority",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "blkio",
+ "Path": ""
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "TimerSlackHigh",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "40000000"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "TimerSlackNormal",
+ "Actions": [
+ {
+ "Name": "SetTimerSlack",
+ "Params":
+ {
+ "Slack": "50000"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "PerfBoost",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "50%",
+ "Clamp": "0"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "PerfClamp",
+ "Actions": [
+ {
+ "Name": "SetClamps",
+ "Params":
+ {
+ "Boost": "0",
+ "Clamp": "30%"
+ }
+ }
+ ]
+ },
+
+ {
+ "Name": "LowMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "16MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "150"
+
+ }
+ }
+ ]
+ },
+ {
+ "Name": "HighMemoryUsage",
+ "Actions": [
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSoftLimit",
+ "Value": "512MB"
+ }
+ },
+ {
+ "Name": "SetAttribute",
+ "Params":
+ {
+ "Name": "MemSwappiness",
+ "Value": "100"
+ }
+ }
+ ]
+ },
+ {
+ "Name": "SystemMemoryProcess",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "memory",
+ "Path": "system"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
new file mode 100644
index 0000000..578f0d3
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.profiles;
+
+// Next: 3
+message TaskProfiles {
+ repeated Attribute attributes = 1 [json_name = "Attributes"];
+ repeated Profile profiles = 2 [json_name = "Profiles"];
+}
+
+// Next: 4
+message Attribute {
+ string name = 1 [json_name = "Name"];
+ string controller = 2 [json_name = "Controller"];
+ string file = 3 [json_name = "File"];
+}
+
+// Next: 3
+message Profile {
+ string name = 1 [json_name = "Name"];
+ repeated Action actions = 2 [json_name = "Actions"];
+}
+
+// Next: 3
+message Action {
+ string name = 1 [json_name = "Name"];
+ map<string, string> params = 2 [json_name = "Params"];
+}
diff --git a/libprocessgroup/profiles/task_profiles_test.h b/libprocessgroup/profiles/task_profiles_test.h
new file mode 100644
index 0000000..32f122d
--- /dev/null
+++ b/libprocessgroup/profiles/task_profiles_test.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "task_profiles.pb.h"
+
+namespace android {
+namespace profiles {
+
+class TaskProfilesTest : public jsonpb::JsonSchemaTest {
+ public:
+ void SetUp() override {
+ JsonSchemaTest::SetUp();
+ task_profiles_ = static_cast<TaskProfiles*>(message());
+ }
+ TaskProfiles* task_profiles_;
+};
+
+TEST_P(TaskProfilesTest, AttributeRequiredFields) {
+ for (int i = 0; i < task_profiles_->attributes_size(); ++i) {
+ auto&& attribute = task_profiles_->attributes(i);
+ EXPECT_FALSE(attribute.name().empty())
+ << "No name for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.controller().empty())
+ << "No controller for attribute #" << i << " in " << file_path_;
+ EXPECT_FALSE(attribute.file().empty())
+ << "No file for attribute #" << i << " in " << file_path_;
+ }
+}
+
+TEST_P(TaskProfilesTest, ProfileRequiredFields) {
+ for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {
+ auto&& profile = task_profiles_->profiles(profile_idx);
+ EXPECT_FALSE(profile.name().empty())
+ << "No name for profile #" << profile_idx << " in " << file_path_;
+ for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {
+ auto&& action = profile.actions(action_idx);
+ EXPECT_FALSE(action.name().empty())
+ << "No name for profiles[" << profile_idx << "].actions[" << action_idx
+ << "] in " << file_path_;
+ }
+ }
+}
+
+} // namespace profiles
+} // namespace android
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
new file mode 100644
index 0000000..bc9aade
--- /dev/null
+++ b/libprocessgroup/profiles/test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using namespace ::android::jsonpb;
+using ::android::base::GetExecutableDirectory;
+
+namespace android {
+namespace profiles {
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+ return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);
+}
+
+// Test suite instantiations
+INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+ MakeTestParam<Cgroups>("/cgroups.recovery.json"),
+ MakeTestParam<TaskProfiles>("/task_profiles.json")));
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
+ ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
+ MakeTestParam<Cgroups>("/cgroups.recovery.json")));
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
+
+} // namespace profiles
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/test_vendor.cpp b/libprocessgroup/profiles/test_vendor.cpp
new file mode 100644
index 0000000..3ec7fcf
--- /dev/null
+++ b/libprocessgroup/profiles/test_vendor.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <jsonpb/json_schema_test.h>
+
+#include "cgroups_test.h"
+#include "task_profiles_test.h"
+
+using ::android::base::GetExecutableDirectory;
+using namespace ::android::jsonpb;
+
+namespace android {
+namespace profiles {
+
+static constexpr const char* kVendorCgroups = "/vendor/etc/cgroups.json";
+static constexpr const char* kVendorTaskProfiles = "/vendor/etc/task_profiles.json";
+
+template <typename T>
+class TestConfig : public JsonSchemaTestConfig {
+ public:
+ TestConfig(const std::string& path) : file_path_(path){};
+ std::unique_ptr<google::protobuf::Message> CreateMessage() const override {
+ return std::make_unique<T>();
+ }
+ std::string file_path() const override { return file_path_; }
+ bool optional() const override {
+ // Ignore when vendor JSON files are missing.
+ return true;
+ }
+
+ private:
+ std::string file_path_;
+};
+
+template <typename T>
+JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {
+ return [path]() { return std::make_unique<TestConfig<T>>(path); };
+}
+
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+INSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,
+ ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));
+
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+INSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,
+ ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));
+
+} // namespace profiles
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/libprocessgroup/profiles/vts_processgroup_validate_test.xml b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
new file mode 100644
index 0000000..21d29cd
--- /dev/null
+++ b/libprocessgroup/profiles/vts_processgroup_validate_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for VtsProcessgroupValidateTest">
+ <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="abort-on-push-failure" value="false"/>
+ <option name="push-group" value="HostDrivenTest.push"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsProcessgroupValidateTest"/>
+ <option name="binary-test-working-directory" value="_32bit::/data/nativetest/" />
+ <option name="binary-test-working-directory" value="_64bit::/data/nativetest64/" />
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_processgroup_validate_test/vts_processgroup_validate_test" />
+ <option name="binary-test-type" value="gtest"/>
+ <option name="binary-test-disable-framework" value="false"/>
+ <option name="test-timeout" value="30s"/>
+ </test>
+</configuration>
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
new file mode 100644
index 0000000..15f8139
--- /dev/null
+++ b/libprocessgroup/sched_policy.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <processgroup/sched_policy.h>
+
+#define LOG_TAG "SchedPolicy"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+#include <cgroup_map.h>
+#include <processgroup/processgroup.h>
+
+using android::base::GetThreadId;
+
+/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
+ * Call this any place a SchedPolicy is used as an input parameter.
+ * Returns the possibly re-mapped policy.
+ */
+static inline SchedPolicy _policy(SchedPolicy p) {
+ return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
+}
+
+#if defined(__ANDROID__)
+
+int set_cpuset_policy(int tid, SchedPolicy policy) {
+ if (tid == 0) {
+ tid = GetThreadId();
+ }
+ policy = _policy(policy);
+
+ switch (policy) {
+ case SP_BACKGROUND:
+ return SetTaskProfiles(tid,
+ {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+ "TimerSlackHigh"},
+ true)
+ ? 0
+ : -1;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ return SetTaskProfiles(tid,
+ {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+ "TimerSlackNormal"},
+ true)
+ ? 0
+ : -1;
+ case SP_TOP_APP:
+ return SetTaskProfiles(tid,
+ {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+ "TimerSlackNormal"},
+ true)
+ ? 0
+ : -1;
+ case SP_SYSTEM:
+ return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
+ case SP_RESTRICTED:
+ return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
+ ? 0
+ : -1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int set_sched_policy(int tid, SchedPolicy policy) {
+ if (tid == 0) {
+ tid = GetThreadId();
+ }
+ policy = _policy(policy);
+
+#if POLICY_DEBUG
+ char statfile[64];
+ char statline[1024];
+ char thread_name[255];
+
+ snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
+ memset(thread_name, 0, sizeof(thread_name));
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(statfile, O_RDONLY | O_CLOEXEC)));
+ if (fd >= 0) {
+ int rc = read(fd, statline, 1023);
+ statline[rc] = 0;
+ char* p = statline;
+ char* q;
+
+ for (p = statline; *p != '('; p++)
+ ;
+ p++;
+ for (q = p; *q != ')'; q++)
+ ;
+
+ strncpy(thread_name, p, (q - p));
+ }
+ switch (policy) {
+ case SP_BACKGROUND:
+ SLOGD("vvv tid %d (%s)", tid, thread_name);
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ case SP_TOP_APP:
+ SLOGD("^^^ tid %d (%s)", tid, thread_name);
+ break;
+ case SP_SYSTEM:
+ SLOGD("/// tid %d (%s)", tid, thread_name);
+ break;
+ case SP_RT_APP:
+ SLOGD("RT tid %d (%s)", tid, thread_name);
+ break;
+ default:
+ SLOGD("??? tid %d (%s)", tid, thread_name);
+ break;
+ }
+#endif
+
+ switch (policy) {
+ case SP_BACKGROUND:
+ return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ case SP_TOP_APP:
+ return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ case SP_RT_APP:
+ return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
+ default:
+ return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
+ }
+
+ return 0;
+}
+
+bool cpusets_enabled() {
+ static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").IsUsable());
+ return enabled;
+}
+
+bool schedboost_enabled() {
+ static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
+ return enabled;
+}
+
+static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
+ auto controller = CgroupMap::GetInstance().FindController(subsys);
+
+ if (!controller.IsUsable()) return -1;
+
+ if (!controller.GetTaskGroup(tid, &subgroup)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return -1;
+ }
+ return 0;
+}
+
+int get_sched_policy(int tid, SchedPolicy* policy) {
+ if (tid == 0) {
+ tid = GetThreadId();
+ }
+
+ std::string group;
+ if (schedboost_enabled()) {
+ if (getCGroupSubsys(tid, "schedtune", group) < 0) return -1;
+ }
+ if (group.empty() && cpusets_enabled()) {
+ if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
+ }
+
+ // TODO: replace hardcoded directories
+ if (group.empty()) {
+ *policy = SP_FOREGROUND;
+ } else if (group == "foreground") {
+ *policy = SP_FOREGROUND;
+ } else if (group == "system-background") {
+ *policy = SP_SYSTEM;
+ } else if (group == "background") {
+ *policy = SP_BACKGROUND;
+ } else if (group == "top-app") {
+ *policy = SP_TOP_APP;
+ } else if (group == "restricted") {
+ *policy = SP_RESTRICTED;
+ } else {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+#else
+
+/* Stubs for non-Android targets. */
+
+int set_sched_policy(int, SchedPolicy) {
+ return 0;
+}
+
+int get_sched_policy(int, SchedPolicy* policy) {
+ *policy = SP_SYSTEM_DEFAULT;
+ return 0;
+}
+
+#endif
+
+const char* get_sched_policy_name(SchedPolicy policy) {
+ policy = _policy(policy);
+ static const char* const kSchedPolicyNames[] = {
+ [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ",
+ [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta",
+ [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs",
+ };
+ static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
+ if (policy < SP_BACKGROUND || policy >= SP_CNT) {
+ return "error";
+ }
+ return kSchedPolicyNames[policy];
+}
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+ name: "libprocessgroup_setup",
+ recovery_available: true,
+ srcs: [
+ "cgroup_map_write.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libjsoncpp",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ export_header_lib_headers: [
+ "libprocessgroup_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..f029c4f
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+ public:
+ CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+ mode_t mode, const std::string& uid, const std::string& gid);
+
+ const format::CgroupController* controller() const { return &controller_; }
+ mode_t mode() const { return mode_; }
+ std::string uid() const { return uid_; }
+ std::string gid() const { return gid_; }
+
+ void set_mounted(bool mounted);
+
+ private:
+ format::CgroupController controller_;
+ mode_t mode_ = 0;
+ std::string uid_;
+ std::string gid_;
+};
+
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..17ea06e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid) {
+ if (mode == 0) {
+ mode = 0755;
+ }
+
+ if (mkdir(path.c_str(), mode) != 0) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ // /acct is a special case when the directory already exists
+ // TODO: check if file mode is already what we want instead of using EROFS
+ if (errno != EROFS) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+ } else {
+ PLOG(ERROR) << "mkdir() failed for " << path;
+ return false;
+ }
+ }
+
+ if (uid.empty()) {
+ return true;
+ }
+
+ passwd* uid_pwd = getpwnam(uid.c_str());
+ if (!uid_pwd) {
+ PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+ return false;
+ }
+
+ uid_t pw_uid = uid_pwd->pw_uid;
+ gid_t gr_gid = -1;
+ if (!gid.empty()) {
+ group* gid_pwd = getgrnam(gid.c_str());
+ if (!gid_pwd) {
+ PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+ return false;
+ }
+ gr_gid = gid_pwd->gr_gid;
+ }
+
+ if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+ PLOG(ERROR) << "lchown() failed for " << path;
+ return false;
+ }
+
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+ std::map<std::string, CgroupDescriptor>* descriptors) {
+ std::vector<CgroupDescriptor> result;
+ std::string json_doc;
+
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+ return false;
+ }
+
+ Json::Reader reader;
+ Json::Value root;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+ return false;
+ }
+
+ if (root.isMember("Cgroups")) {
+ const Json::Value& cgroups = root["Cgroups"];
+ for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+ std::string name = cgroups[i]["Controller"].asString();
+ auto iter = descriptors->find(name);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ name, CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+ } else {
+ iter->second = CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+ }
+ }
+ }
+
+ if (root.isMember("Cgroups2")) {
+ const Json::Value& cgroups2 = root["Cgroups2"];
+ auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ CGROUPV2_CONTROLLER_NAME,
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+ } else {
+ iter->second =
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString());
+ }
+ }
+
+ return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+ // load system cgroup descriptors
+ if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+ return false;
+ }
+
+ // load vendor cgroup descriptors if the file exists
+ if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+ !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+ return false;
+ }
+
+ return true;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+ const format::CgroupController* controller = descriptor.controller();
+
+ // mkdir <path> [mode] [owner] [group]
+ if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ return false;
+ }
+
+ int result;
+ if (controller->version() == 2) {
+ result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr);
+ } else {
+ // Unfortunately historically cpuset controller was mounted using a mount command
+ // different from all other controllers. This results in controller attributes not
+ // to be prepended with controller name. For example this way instead of
+ // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+ // the system currently expects.
+ if (!strcmp(controller->name(), "cpuset")) {
+ // mount cpuset none /dev/cpuset nodev noexec nosuid
+ result = mount("none", controller->path(), controller->name(),
+ MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+ } else {
+ // mount cgroup none <path> nodev noexec nosuid <controller>
+ result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ controller->name());
+ }
+ }
+
+ if (result < 0) {
+ PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+ return false;
+ }
+
+ return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+ return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ format::CgroupFile fl;
+ fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+ fl.controller_count_ = descriptors.size();
+ int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ for (const auto& [name, descriptor] : descriptors) {
+ ret = TEMP_FAILURE_RETRY(
+ write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+ const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid)
+ : controller_(version, 0, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+void CgroupDescriptor::set_mounted(bool mounted) {
+ uint32_t flags = controller_.flags();
+ if (mounted) {
+ flags |= CGROUPRC_CONTROLLER_FLAG_MOUNTED;
+ } else {
+ flags &= ~CGROUPRC_CONTROLLER_FLAG_MOUNTED;
+ }
+ controller_.set_flags(flags);
+}
+
+} // namespace cgrouprc
+} // namespace android
+
+bool CgroupSetup() {
+ using namespace android::cgrouprc;
+
+ std::map<std::string, CgroupDescriptor> descriptors;
+
+ if (getpid() != 1) {
+ LOG(ERROR) << "Cgroup setup can be done only by init process";
+ return false;
+ }
+
+ // Make sure we do this only one time. No need for std::call_once because
+ // init is a single-threaded process
+ if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+ LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+ return true;
+ }
+
+ // load cgroups.json file
+ if (!ReadDescriptors(&descriptors)) {
+ LOG(ERROR) << "Failed to load cgroup description file";
+ return false;
+ }
+
+ // setup cgroups
+ for (auto& [name, descriptor] : descriptors) {
+ if (SetupCgroup(descriptor)) {
+ descriptor.set_mounted(true);
+ } else {
+ // issue a warning and proceed with the next cgroup
+ LOG(WARNING) << "Failed to setup " << name << " cgroup";
+ }
+ }
+
+ // mkdir <CGROUPS_RC_DIR> 0711 system system
+ if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+ LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+ // process memory. This optimizes performance, memory usage
+ // and limits infrormation shared with unprivileged processes
+ // to the minimum subset of information from cgroups.json
+ if (!WriteRcFile(descriptors)) {
+ LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // chmod 0644 <CGROUPS_RC_PATH>
+ if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+ PLOG(ERROR) << "fchmodat() failed";
+ return false;
+ }
+
+ return true;
+}
diff --git a/mkbootimg/mkbootimg_dummy.cpp b/libprocessgroup/setup/include/processgroup/setup.h
similarity index 70%
copy from mkbootimg/mkbootimg_dummy.cpp
copy to libprocessgroup/setup/include/processgroup/setup.h
index 410d379..6ea1979 100644
--- a/mkbootimg/mkbootimg_dummy.cpp
+++ b/libprocessgroup/setup/include/processgroup/setup.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <abi_check/mkbootimg_abi_check.h>
+#pragma once
-void mkbootimg_dummy(boot_img_hdr* hdr) {
- // TODO: Hack to trigger abi checks, remove this.
- if (hdr) {
- hdr--;
- }
-}
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
new file mode 100644
index 0000000..aee5f0c
--- /dev/null
+++ b/libprocessgroup/task_profiles.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <fcntl.h>
+#include <task_profiles.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include <cutils/android_filesystem_config.h>
+
+#include <json/reader.h>
+#include <json/value.h>
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+#include <sys/prctl.h>
+#endif
+
+using android::base::GetThreadId;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::base::WriteStringToFile;
+
+#define TASK_PROFILE_DB_FILE "/etc/task_profiles.json"
+#define TASK_PROFILE_DB_VENDOR_FILE "/vendor/etc/task_profiles.json"
+
+bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
+ std::string subgroup;
+ if (!controller()->GetTaskGroup(tid, &subgroup)) {
+ return false;
+ }
+
+ if (path == nullptr) {
+ return true;
+ }
+
+ if (subgroup.empty()) {
+ *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
+ } else {
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+ file_name_.c_str());
+ }
+ return true;
+}
+
+bool SetClampsAction::ExecuteForProcess(uid_t, pid_t) const {
+ // TODO: add support when kernel supports util_clamp
+ LOG(WARNING) << "SetClampsAction::ExecuteForProcess is not supported";
+ return false;
+}
+
+bool SetClampsAction::ExecuteForTask(int) const {
+ // TODO: add support when kernel supports util_clamp
+ LOG(WARNING) << "SetClampsAction::ExecuteForTask is not supported";
+ return false;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+bool SetTimerSlackAction::IsTimerSlackSupported(int tid) {
+ auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
+
+ return (access(file.c_str(), W_OK) == 0);
+}
+
+bool SetTimerSlackAction::ExecuteForTask(int tid) const {
+ static bool sys_supports_timerslack = IsTimerSlackSupported(tid);
+
+ // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
+ // TODO: once we've backported this, log if the open(2) fails.
+ if (sys_supports_timerslack) {
+ auto file = StringPrintf("/proc/%d/timerslack_ns", tid);
+ if (!WriteStringToFile(std::to_string(slack_), file)) {
+ if (errno == ENOENT) {
+ // This happens when process is already dead
+ return true;
+ }
+ PLOG(ERROR) << "set_timerslack_ns write failed";
+ }
+ }
+
+ // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
+ if (tid == 0 || tid == GetThreadId()) {
+ if (prctl(PR_SET_TIMERSLACK, slack_) == -1) {
+ PLOG(ERROR) << "set_timerslack_ns prctl failed";
+ }
+ }
+
+ return true;
+}
+
+#endif
+
+bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
+ return ExecuteForTask(pid);
+}
+
+bool SetAttributeAction::ExecuteForTask(int tid) const {
+ std::string path;
+
+ if (!attribute_->GetPathForTask(tid, &path)) {
+ LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+ return false;
+ }
+
+ if (!WriteStringToFile(value_, path)) {
+ PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
+ return false;
+ }
+
+ return true;
+}
+
+bool SetCgroupAction::IsAppDependentPath(const std::string& path) {
+ return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
+}
+
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
+ : controller_(c), path_(p) {
+ // file descriptors for app-dependent paths can't be cached
+ if (IsAppDependentPath(path_)) {
+ // file descriptor is not cached
+ fd_.reset(FDS_APP_DEPENDENT);
+ return;
+ }
+
+ // file descriptor can be cached later on request
+ fd_.reset(FDS_NOT_CACHED);
+}
+
+void SetCgroupAction::EnableResourceCaching() {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (fd_ != FDS_NOT_CACHED) {
+ return;
+ }
+
+ std::string tasks_path = controller_.GetTasksFilePath(path_);
+
+ if (access(tasks_path.c_str(), W_OK) != 0) {
+ // file is not accessible
+ fd_.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
+ fd_.reset(FDS_INACCESSIBLE);
+ return;
+ }
+
+ fd_ = std::move(fd);
+}
+
+void SetCgroupAction::DropResourceCaching() {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (fd_ == FDS_NOT_CACHED) {
+ return;
+ }
+
+ fd_.reset(FDS_NOT_CACHED);
+}
+
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
+ if (tid <= 0) {
+ return true;
+ }
+
+ std::string value = std::to_string(tid);
+
+ if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
+ // If the thread is in the process of exiting, don't flag an error
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (IsFdValid()) {
+ // fd is cached, reuse it
+ if (!AddTidToCgroup(pid, fd_)) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return false;
+ }
+ return true;
+ }
+
+ if (fd_ == FDS_INACCESSIBLE) {
+ // no permissions to access the file, ignore
+ return true;
+ }
+
+ // this is app-dependent path and fd is not cached or cached fd can't be used
+ std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
+ unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (tmp_fd < 0) {
+ PLOG(WARNING) << "Failed to open " << procs_path;
+ return false;
+ }
+ if (!AddTidToCgroup(pid, tmp_fd)) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return false;
+ }
+
+ return true;
+}
+
+bool SetCgroupAction::ExecuteForTask(int tid) const {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (IsFdValid()) {
+ // fd is cached, reuse it
+ if (!AddTidToCgroup(tid, fd_)) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return false;
+ }
+ return true;
+ }
+
+ if (fd_ == FDS_INACCESSIBLE) {
+ // no permissions to access the file, ignore
+ return true;
+ }
+
+ if (fd_ == FDS_APP_DEPENDENT) {
+ // application-dependent path can't be used with tid
+ PLOG(ERROR) << "Application profile can't be applied to a thread";
+ return false;
+ }
+
+ // fd was not cached because cached fd can't be used
+ std::string tasks_path = controller()->GetTasksFilePath(path_);
+ unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
+ if (tmp_fd < 0) {
+ PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
+ return false;
+ }
+ if (!AddTidToCgroup(tid, tmp_fd)) {
+ LOG(ERROR) << "Failed to add task into cgroup";
+ return false;
+ }
+
+ return true;
+}
+
+bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ for (const auto& element : elements_) {
+ if (!element->ExecuteForProcess(uid, pid)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool TaskProfile::ExecuteForTask(int tid) const {
+ if (tid == 0) {
+ tid = GetThreadId();
+ }
+ for (const auto& element : elements_) {
+ if (!element->ExecuteForTask(tid)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TaskProfile::EnableResourceCaching() {
+ if (res_cached_) {
+ return;
+ }
+
+ for (auto& element : elements_) {
+ element->EnableResourceCaching();
+ }
+
+ res_cached_ = true;
+}
+
+void TaskProfile::DropResourceCaching() {
+ if (!res_cached_) {
+ return;
+ }
+
+ for (auto& element : elements_) {
+ element->DropResourceCaching();
+ }
+
+ res_cached_ = false;
+}
+
+void TaskProfiles::DropResourceCaching() const {
+ for (auto& iter : profiles_) {
+ iter.second->DropResourceCaching();
+ }
+}
+
+TaskProfiles& TaskProfiles::GetInstance() {
+ // Deliberately leak this object to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* instance = new TaskProfiles;
+ return *instance;
+}
+
+TaskProfiles::TaskProfiles() {
+ // load system task profiles
+ if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_FILE << " for [" << getpid() << "] failed";
+ }
+
+ // load vendor task profiles if the file exists
+ if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&
+ !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {
+ LOG(ERROR) << "Loading " << TASK_PROFILE_DB_VENDOR_FILE << " for [" << getpid()
+ << "] failed";
+ }
+}
+
+bool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {
+ std::string json_doc;
+
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ LOG(ERROR) << "Failed to read task profiles from " << file_name;
+ return false;
+ }
+
+ Json::Reader reader;
+ Json::Value root;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse task profiles: " << reader.getFormattedErrorMessages();
+ return false;
+ }
+
+ const Json::Value& attr = root["Attributes"];
+ for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {
+ std::string name = attr[i]["Name"].asString();
+ std::string controller_name = attr[i]["Controller"].asString();
+ std::string file_attr = attr[i]["File"].asString();
+
+ if (attributes_.find(name) == attributes_.end()) {
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
+ attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
+ } else {
+ LOG(WARNING) << "Controller " << controller_name << " is not found";
+ }
+ } else {
+ LOG(WARNING) << "Attribute " << name << " is already defined";
+ }
+ }
+
+ std::map<std::string, std::string> params;
+
+ const Json::Value& profiles_val = root["Profiles"];
+ for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
+ const Json::Value& profile_val = profiles_val[i];
+
+ std::string profile_name = profile_val["Name"].asString();
+ const Json::Value& actions = profile_val["Actions"];
+ auto profile = std::make_unique<TaskProfile>();
+
+ for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
+ const Json::Value& action_val = actions[act_idx];
+ std::string action_name = action_val["Name"].asString();
+ const Json::Value& params_val = action_val["Params"];
+ if (action_name == "JoinCgroup") {
+ std::string controller_name = params_val["Controller"].asString();
+ std::string path = params_val["Path"].asString();
+
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
+ profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+ } else {
+ LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
+ }
+ } else if (action_name == "SetTimerSlack") {
+ std::string slack_value = params_val["Slack"].asString();
+ char* end;
+ unsigned long slack;
+
+ slack = strtoul(slack_value.c_str(), &end, 10);
+ if (end > slack_value.c_str()) {
+ profile->Add(std::make_unique<SetTimerSlackAction>(slack));
+ } else {
+ LOG(WARNING) << "SetTimerSlack: invalid parameter: " << slack_value;
+ }
+ } else if (action_name == "SetAttribute") {
+ std::string attr_name = params_val["Name"].asString();
+ std::string attr_value = params_val["Value"].asString();
+
+ auto iter = attributes_.find(attr_name);
+ if (iter != attributes_.end()) {
+ profile->Add(
+ std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
+ } else {
+ LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
+ }
+ } else if (action_name == "SetClamps") {
+ std::string boost_value = params_val["Boost"].asString();
+ std::string clamp_value = params_val["Clamp"].asString();
+ char* end;
+ unsigned long boost;
+
+ boost = strtoul(boost_value.c_str(), &end, 10);
+ if (end > boost_value.c_str()) {
+ unsigned long clamp = strtoul(clamp_value.c_str(), &end, 10);
+ if (end > clamp_value.c_str()) {
+ profile->Add(std::make_unique<SetClampsAction>(boost, clamp));
+ } else {
+ LOG(WARNING) << "SetClamps: invalid parameter " << clamp_value;
+ }
+ } else {
+ LOG(WARNING) << "SetClamps: invalid parameter: " << boost_value;
+ }
+ } else {
+ LOG(WARNING) << "Unknown profile action: " << action_name;
+ }
+ }
+ profiles_[profile_name] = std::move(profile);
+ }
+
+ return true;
+}
+
+TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
+ auto iter = profiles_.find(name);
+
+ if (iter != profiles_.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
+}
+
+const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+ auto iter = attributes_.find(name);
+
+ if (iter != attributes_.end()) {
+ return iter->second.get();
+ }
+ return nullptr;
+}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
new file mode 100644
index 0000000..891d5b5
--- /dev/null
+++ b/libprocessgroup/task_profiles.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <cgroup_map.h>
+
+class ProfileAttribute {
+ public:
+ ProfileAttribute(const CgroupController& controller, const std::string& file_name)
+ : controller_(controller), file_name_(file_name) {}
+
+ const CgroupController* controller() const { return &controller_; }
+ const std::string& file_name() const { return file_name_; }
+
+ bool GetPathForTask(int tid, std::string* path) const;
+
+ private:
+ CgroupController controller_;
+ std::string file_name_;
+};
+
+// Abstract profile element
+class ProfileAction {
+ public:
+ virtual ~ProfileAction() {}
+
+ // Default implementations will fail
+ virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
+ virtual bool ExecuteForTask(int) const { return false; };
+
+ virtual void EnableResourceCaching() {}
+ virtual void DropResourceCaching() {}
+};
+
+// Profile actions
+class SetClampsAction : public ProfileAction {
+ public:
+ SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
+
+ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ virtual bool ExecuteForTask(int tid) const;
+
+ protected:
+ int boost_;
+ int clamp_;
+};
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+class SetTimerSlackAction : public ProfileAction {
+ public:
+ SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
+
+ virtual bool ExecuteForTask(int tid) const;
+
+ private:
+ unsigned long slack_;
+
+ static bool IsTimerSlackSupported(int tid);
+};
+
+#else
+
+class SetTimerSlackAction : public ProfileAction {
+ public:
+ SetTimerSlackAction(unsigned long) noexcept {}
+
+ virtual bool ExecuteForTask(int) const { return true; }
+};
+
+#endif
+
+// Set attribute profile element
+class SetAttributeAction : public ProfileAction {
+ public:
+ SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+ : attribute_(attribute), value_(value) {}
+
+ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ virtual bool ExecuteForTask(int tid) const;
+
+ private:
+ const ProfileAttribute* attribute_;
+ std::string value_;
+};
+
+// Set cgroup profile element
+class SetCgroupAction : public ProfileAction {
+ public:
+ SetCgroupAction(const CgroupController& c, const std::string& p);
+
+ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ virtual bool ExecuteForTask(int tid) const;
+ virtual void EnableResourceCaching();
+ virtual void DropResourceCaching();
+
+ const CgroupController* controller() const { return &controller_; }
+ std::string path() const { return path_; }
+
+ private:
+ enum FdState {
+ FDS_INACCESSIBLE = -1,
+ FDS_APP_DEPENDENT = -2,
+ FDS_NOT_CACHED = -3,
+ };
+
+ CgroupController controller_;
+ std::string path_;
+ android::base::unique_fd fd_;
+ mutable std::mutex fd_mutex_;
+
+ static bool IsAppDependentPath(const std::string& path);
+ static bool AddTidToCgroup(int tid, int fd);
+
+ bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
+};
+
+class TaskProfile {
+ public:
+ TaskProfile() : res_cached_(false) {}
+
+ void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
+
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ bool ExecuteForTask(int tid) const;
+ void EnableResourceCaching();
+ void DropResourceCaching();
+
+ private:
+ bool res_cached_;
+ std::vector<std::unique_ptr<ProfileAction>> elements_;
+};
+
+class TaskProfiles {
+ public:
+ // Should be used by all users
+ static TaskProfiles& GetInstance();
+
+ TaskProfile* GetProfile(const std::string& name) const;
+ const ProfileAttribute* GetAttribute(const std::string& name) const;
+ void DropResourceCaching() const;
+
+ private:
+ std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
+ std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+
+ TaskProfiles();
+
+ bool Load(const CgroupMap& cg_map, const std::string& file_name);
+};
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 3771f9f..b6ec3cb 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -17,11 +17,13 @@
#pragma once
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <functional>
#include <string>
+#include <vector>
#include <android-base/file.h>
@@ -34,6 +36,7 @@
uint64_t end_addr;
uint16_t flags;
uint64_t pgoff;
+ ino_t inode;
char* next_line = content;
char* p;
@@ -122,18 +125,25 @@
return false;
}
// inode
- if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+ inode = strtoull(p, &end, 10);
+ if (end == p) {
return false;
}
+ p = end;
+
+ if (*p != '\0' && !pass_space()) {
+ return false;
+ }
+
// filename
- callback(start_addr, end_addr, flags, pgoff, p);
+ callback(start_addr, end_addr, flags, pgoff, inode, p);
}
return true;
}
-inline bool ReadMapFile(
- const std::string& map_file,
- const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadMapFile(const std::string& map_file,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
std::string content;
if (!android::base::ReadFileToString(map_file, &content)) {
return false;
@@ -141,11 +151,30 @@
return ReadMapFileContent(&content[0], callback);
}
-inline bool ReadProcessMaps(
- pid_t pid,
- const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, const char*)>& callback) {
+inline bool ReadProcessMaps(pid_t pid,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
}
+struct MapInfo {
+ uint64_t start;
+ uint64_t end;
+ uint16_t flags;
+ uint64_t pgoff;
+ ino_t inode;
+ std::string name;
+
+ MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode), name(name) {}
+};
+
+inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
+ return ReadProcessMaps(
+ pid, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
+}
+
} /* namespace procinfo */
} /* namespace android */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
index d9e8a4d..eba4fd0 100644
--- a/libprocinfo/process_map_benchmark.cpp
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -17,6 +17,7 @@
#include <procinfo/process_map.h>
#include <string.h>
+#include <sys/types.h>
#include <string>
@@ -27,24 +28,14 @@
#include <benchmark/benchmark.h>
-struct MapInfo {
- uint64_t start;
- uint64_t end;
- uint16_t flags;
- uint64_t pgoff;
- const std::string name;
-
- MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
- : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
static void BM_ReadMapFile(benchmark::State& state) {
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
for (auto _ : state) {
- std::vector<MapInfo> maps;
- android::procinfo::ReadMapFile(
- map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
- const char* name) { maps.emplace_back(start, end, flags, pgoff, name); });
+ std::vector<android::procinfo::MapInfo> maps;
+ android::procinfo::ReadMapFile(map_file, [&](uint64_t start, uint64_t end, uint16_t flags,
+ uint64_t pgoff, ino_t inode, const char* name) {
+ maps.emplace_back(start, end, flags, pgoff, inode, name);
+ });
CHECK_EQ(maps.size(), 2043u);
}
}
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 4b93c5b..562d864 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -22,39 +22,44 @@
#include <gtest/gtest.h>
-struct MapInfo {
- uint64_t start;
- uint64_t end;
- uint16_t flags;
- uint64_t pgoff;
- const std::string name;
-
- MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name)
- : start(start), end(end), flags(flags), pgoff(pgoff), name(name) {}
-};
-
-TEST(process_map, smoke) {
+TEST(process_map, ReadMapFile) {
std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
- std::vector<MapInfo> maps;
+ std::vector<android::procinfo::MapInfo> maps;
ASSERT_TRUE(android::procinfo::ReadMapFile(
- map_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
- const char* name) { maps.emplace_back(start, end, flags, pgoff, name); }));
+ map_file,
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
ASSERT_EQ(2043u, maps.size());
ASSERT_EQ(maps[0].start, 0x12c00000ULL);
ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
ASSERT_EQ(maps[0].pgoff, 0ULL);
+ ASSERT_EQ(maps[0].inode, 10267643UL);
ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
ASSERT_EQ(maps[876].pgoff, 0ULL);
+ ASSERT_EQ(maps[876].inode, 2407UL);
ASSERT_EQ(maps[876].name, "/system/lib64/libutils.so");
ASSERT_EQ(maps[1260].start, 0x70e96fa000ULL);
ASSERT_EQ(maps[1260].end, 0x70e96fb000ULL);
ASSERT_EQ(maps[1260].flags, PROT_READ);
ASSERT_EQ(maps[1260].pgoff, 0ULL);
+ ASSERT_EQ(maps[1260].inode, 10266154UL);
ASSERT_EQ(maps[1260].name,
"[anon:dalvik-classes.dex extracted in memory from "
"/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
}
+
+TEST(process_map, ReadProcessMaps) {
+ std::vector<android::procinfo::MapInfo> maps;
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(
+ getpid(),
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) { maps.emplace_back(start, end, flags, pgoff, inode, name); }));
+ ASSERT_GT(maps.size(), 0u);
+ maps.clear();
+ ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
+ ASSERT_GT(maps.size(), 0u);
+}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 8ad339f..2ec4754 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -67,3 +67,18 @@
cflags: ["-Werror"],
}
+
+python_binary_host {
+ name: "simg_dump.py",
+ main: "simg_dump.py",
+ srcs: ["simg_dump.py"],
+ version: {
+ py2: {
+ embedded_launcher: true,
+ enabled: true,
+ },
+ py3: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
deleted file mode 100644
index 05e68bc..0000000
--- a/libsparse/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2010 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := simg_dump.py
-LOCAL_SRC_FILES := simg_dump.py
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_IS_HOST_MODULE := true
-LOCAL_CFLAGS := -Werror
-include $(BUILD_PREBUILT)
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index fe314b3..5b8179f 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -508,7 +508,7 @@
out->len = len;
out->block_size = block_size;
- out->cur_out_ptr = 0ll;
+ out->cur_out_ptr = 0LL;
out->chunk_cnt = 0;
out->crc32 = 0;
out->use_crc = crc;
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index cb288c5..24c6379 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -188,7 +188,7 @@
int (*write)(void* priv, const void* data, size_t len,
unsigned int block, unsigned int nr_blocks),
void* priv) {
- int ret;
+ int ret = 0;
int chunks;
struct chunk_data chk;
struct output_file* out;
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index a9832db..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -18,14 +18,17 @@
#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
#include <log/log_event_list.h>
+#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop();
+void note_log_drop(int error, int atom_tag);
void stats_log_close();
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
#ifdef __cplusplus
}
@@ -51,10 +54,6 @@
explicit stats_event_list(int tag) : ret(0) {
ctx = create_android_logger(static_cast<uint32_t>(tag));
}
- explicit stats_event_list(log_msg& log_msg) : ret(0) {
- ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
- log_msg.entry.len - sizeof(uint32_t));
- }
~stats_event_list() { android_log_destroy(&ctx); }
int close() {
@@ -244,8 +243,13 @@
return ret >= 0;
}
- android_log_list_element read() { return android_log_read_next(ctx); }
- android_log_list_element peek() { return android_log_peek_next(ctx); }
+ bool AppendCharArray(const char* value, size_t len) {
+ int retval = android_log_write_char_array(ctx, value, len);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
};
#endif
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 72770d4..ae12cbe 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -41,7 +41,7 @@
extern struct android_log_transport_write statsdLoggerWrite;
static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
// Similar to create_android_logger(), but instead of allocation a new buffer,
// this function resets the buffer for resuse.
@@ -120,8 +120,8 @@
return retValue;
}
-void note_log_drop() {
- statsdLoggerWrite.noteDrop();
+void note_log_drop(int error, int tag) {
+ statsdLoggerWrite.noteDrop(error, tag);
}
void stats_log_close() {
@@ -193,3 +193,47 @@
errno = save_errno;
return ret;
}
+
+static inline void copy4LE(uint8_t* buf, uint32_t val) {
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+}
+
+// Note: this function differs from android_log_write_string8_len in that the length passed in
+// should be treated as actual length and not max length.
+int android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {
+ size_t needed;
+ ssize_t len = actual_len;
+ android_log_context_internal* context;
+
+ context = (android_log_context_internal*)ctx;
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+ if (context->overflow) {
+ return -EIO;
+ }
+ if (!value) {
+ value = "";
+ len = 0;
+ }
+ needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ /* Truncate string for delivery */
+ len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+ if (len <= 0) {
+ context->overflow = true;
+ return -EIO;
+ }
+ }
+ context->count[context->list_nest_depth]++;
+ context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+ copy4LE(&context->storage[context->pos + 1], len);
+ if (len) {
+ memcpy(&context->storage[context->pos + 5], value, len);
+ }
+ context->pos += needed;
+ return len;
+}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 88f7d44..b778f92 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -31,6 +31,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
@@ -46,8 +47,18 @@
#endif
#endif
+#ifndef htole64
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole64(x) (x)
+#else
+#define htole64(x) __bswap_64(x)
+#endif
+#endif
+
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
+static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
void statsd_writer_init_lock() {
/*
@@ -150,8 +161,10 @@
return 1;
}
-static void statsdNoteDrop() {
+static void statsdNoteDrop(int error, int tag) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+ atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
}
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -200,11 +213,17 @@
if (sock >= 0) {
int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot) {
- android_log_event_int_t buffer;
+ android_log_event_long_t buffer;
header.id = LOG_ID_STATS;
- buffer.header.tag = htole32(LIBLOG_LOG_TAG);
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
+ // store the last log error in the tag field. This tag field is not used by statsd.
+ buffer.header.tag = htole32(atomic_load(&log_error));
+ buffer.payload.type = EVENT_TYPE_LONG;
+ // format:
+ // |atom_tag|dropped_count|
+ int64_t composed_long = atomic_load(&atom_tag);
+ // Send 2 int32's via an int64.
+ composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+ buffer.payload.data = htole64(composed_long);
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 7289441..fe2d37c 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
- void (*noteDrop)();
+ void (*noteDrop)(int error, int tag);
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsync/Android.bp b/libsync/Android.bp
index e56f8ba..c996e1b 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -23,6 +23,7 @@
cc_library {
name: "libsync",
recovery_available: true,
+ native_bridge_supported: true,
defaults: ["libsync_defaults"],
}
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 2e22b43..b265b61 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -3,6 +3,7 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
export_include_dirs: ["include"],
target: {
diff --git a/libsystem/OWNERS b/libsystem/OWNERS
index aeb160c..fdea804 100644
--- a/libsystem/OWNERS
+++ b/libsystem/OWNERS
@@ -1,2 +1,9 @@
-jessehall@google.com
-olv@google.com
+# graphics/composer
+adyabr@google.com
+lpy@google.com
+marissaw@google.com
+stoza@google.com
+vhau@google.com
+
+# camera
+etalvala@google.com
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 29d23c8..ccda5d1 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,4 +1,4 @@
-cc_library_shared {
+cc_library {
name: "libsysutils",
vendor_available: true,
vndk: {
@@ -26,6 +26,19 @@
],
export_include_dirs: ["include"],
+
+ tidy: true,
+ tidy_checks: [
+ "-*",
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
+ tidy_checks_as_errors: [
+ "cert-*",
+ "clang-analyzer-security*",
+ "android-*",
+ ],
}
cc_test {
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
index 645baf4..4c99361 100644
--- a/libsysutils/include/sysutils/OWNERS
+++ b/libsysutils/include/sysutils/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+include ../../src/OWNERS
+
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index b07853a..523584a 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -26,7 +26,7 @@
#include <sysutils/FrameworkListener.h>
#include <sysutils/SocketClient.h>
-static const int CMD_BUF_SIZE = 1024;
+static const int CMD_BUF_SIZE = 4096;
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9dc2699..8fe7854 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -39,9 +39,12 @@
const int LOCAL_QLOG_NL_EVENT = 112;
const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
+#include <android-base/parseint.h>
#include <log/log.h>
#include <sysutils/NetlinkEvent.h>
+using android::base::ParseInt;
+
NetlinkEvent::NetlinkEvent() {
mAction = Action::kUnknown;
memset(mParams, 0, sizeof(mParams));
@@ -301,8 +304,9 @@
raw = (char*)nlAttrData(payload);
}
- char* hex = (char*) calloc(1, 5 + (len * 2));
- strcpy(hex, "HEX=");
+ size_t hexSize = 5 + (len * 2);
+ char* hex = (char*)calloc(1, hexSize);
+ strlcpy(hex, "HEX=", hexSize);
for (int i = 0; i < len; i++) {
hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
@@ -474,23 +478,20 @@
struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
- // Construct "SERVERS=<comma-separated string of DNS addresses>".
- static const char kServerTag[] = "SERVERS=";
- static const size_t kTagLength = strlen(kServerTag);
+ // Construct a comma-separated string of DNS addresses.
// Reserve sufficient space for an IPv6 link-local address: all but the
// last address are followed by ','; the last is followed by '\0'.
static const size_t kMaxSingleAddressLength =
INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
- const size_t bufsize = kTagLength + numaddrs * kMaxSingleAddressLength;
+ const size_t bufsize = numaddrs * kMaxSingleAddressLength;
char *buf = (char *) malloc(bufsize);
if (!buf) {
SLOGE("RDNSS option: out of memory\n");
return false;
}
- strcpy(buf, kServerTag);
- size_t pos = kTagLength;
struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);
+ size_t pos = 0;
for (int i = 0; i < numaddrs; i++) {
if (i > 0) {
buf[pos++] = ',';
@@ -508,7 +509,8 @@
mSubsystem = strdup("net");
asprintf(&mParams[0], "INTERFACE=%s", ifname);
asprintf(&mParams[1], "LIFETIME=%u", lifetime);
- mParams[2] = buf;
+ asprintf(&mParams[2], "SERVERS=%s", buf);
+ free(buf);
} else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
// TODO: support DNSSL.
} else {
@@ -634,7 +636,9 @@
else if (!strcmp(a, "change"))
mAction = Action::kChange;
} else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
- mSeq = atoi(a);
+ if (!ParseInt(a, &mSeq)) {
+ SLOGE("NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s", a);
+ }
} else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
mSubsystem = strdup(a);
} else if (param_idx < NL_PARAMS_MAX) {
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index 645baf4..c65a40d 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1 +1,2 @@
-per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
+per-file OWNERS,Netlink* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, satk@google.com
+
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index ded5adb..9780606 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -95,7 +95,7 @@
} else if (!mListen)
mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
- if (pipe(mCtrlPipe)) {
+ if (pipe2(mCtrlPipe, O_CLOEXEC)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 14f82c7..73237e6 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -94,7 +94,9 @@
"DexFile.cpp",
"DexFiles.cpp",
],
- exclude_shared_libs: ["libdexfile"],
+ exclude_shared_libs: [
+ "libdexfile_support",
+ ],
},
recovery: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
@@ -102,7 +104,9 @@
"DexFile.cpp",
"DexFiles.cpp",
],
- exclude_shared_libs: ["libdexfile"],
+ exclude_shared_libs: [
+ "libdexfile_support",
+ ],
},
},
@@ -127,7 +131,7 @@
shared_libs: [
"libbase",
- "libdexfile",
+ "libdexfile_support",
"liblog",
"liblzma",
],
@@ -154,6 +158,7 @@
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
+ isolated: true,
srcs: [
"tests/ArmExidxDecodeTest.cpp",
@@ -176,14 +181,18 @@
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
+ "tests/IsolatedSettings.cpp",
"tests/JitDebugTest.cpp",
"tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoCreateMemoryTest.cpp",
+ "tests/MapInfoGetBuildIDTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
+ "tests/MapInfoTest.cpp",
"tests/MapsTest.cpp",
"tests/MemoryBufferTest.cpp",
+ "tests/MemoryCacheTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
@@ -198,6 +207,7 @@
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
+ "tests/TestUtils.cpp",
"tests/UnwindOfflineTest.cpp",
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
@@ -213,7 +223,7 @@
"liblog",
"liblzma",
"libunwindstack",
- "libdexfile",
+ "libdexfile_support",
],
static_libs: [
@@ -229,11 +239,16 @@
"tests/files/offline/debug_frame_first_x86/*",
"tests/files/offline/debug_frame_load_bias_arm/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
+ "tests/files/offline/invalid_elf_offset_arm/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/load_bias_ro_rx_x86_64/*",
"tests/files/offline/offset_arm/*",
+ "tests/files/offline/shared_lib_in_apk_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
+ "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
@@ -310,6 +325,29 @@
],
}
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+ name: "unwind_benchmarks",
+ host_supported: true,
+ defaults: ["libunwindstack_flags"],
+
+ // Disable optimizations so that all of the calls are not optimized away.
+ cflags: [
+ "-O0",
+ ],
+
+ srcs: [
+ "benchmarks/unwind_benchmarks.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libunwindstack",
+ ],
+}
+
// Generates the elf data for use in the tests for .gnu_debugdata frames.
// Once these files are generated, use the xz command to compress the data.
cc_binary_host {
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index 8ec560c..eaf867f 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -23,13 +23,7 @@
#include <memory>
#include <android-base/unique_fd.h>
-
-#include <dex/class_accessor-inl.h>
-#include <dex/code_item_accessors-inl.h>
-#include <dex/compact_dex_file.h>
-#include <dex/dex_file-inl.h>
-#include <dex/dex_file_loader.h>
-#include <dex/standard_dex_file.h>
+#include <art_api/dex_file_support.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
@@ -38,169 +32,71 @@
namespace unwindstack {
-DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
+std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+ MapInfo* info) {
if (!info->name.empty()) {
- std::unique_ptr<DexFileFromFile> dex_file(new DexFileFromFile);
- if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
- return dex_file.release();
+ std::unique_ptr<DexFile> dex_file =
+ DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
+ if (dex_file) {
+ return dex_file;
}
}
-
- std::unique_ptr<DexFileFromMemory> dex_file(new DexFileFromMemory);
- if (dex_file->Open(dex_file_offset_in_memory, memory)) {
- return dex_file.release();
- }
- return nullptr;
-}
-
-DexFileFromFile::~DexFileFromFile() {
- if (size_ != 0) {
- munmap(mapped_memory_, size_);
- }
+ return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
}
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
uint64_t* method_offset) {
- if (dex_file_ == nullptr) {
+ art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
+ if (method_info.offset == 0) {
return false;
}
-
- if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
- return false; // The DEX offset is not within the bytecode of this dex file.
- }
-
- if (dex_file_->IsCompactDexFile()) {
- // The data section of compact dex files might be shared.
- // Check the subrange unique to this compact dex.
- const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
- uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
- uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
- if (dex_offset < begin || dex_offset >= end) {
- return false; // The DEX offset is not within the bytecode of this dex file.
- }
- }
-
- // The method data is cached in a std::map indexed by method end offset and
- // contains the start offset and the method member index.
- // Only cache the method data as it is searched. Do not read the entire
- // set of method data into the cache at once.
- // This is done because many unwinds only find a single frame with dex file
- // info, so reading the entire method data is wasteful. However, still cache
- // the data so that anything doing multiple unwinds will have this data
- // cached for future use.
-
- // First look in the method cache.
- auto entry = method_cache_.upper_bound(dex_offset);
- if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
- *method_name = dex_file_->PrettyMethod(entry->second.second, false);
- *method_offset = dex_offset - entry->second.first;
- return true;
- }
-
- // Check the methods we haven't cached.
- for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
- art::ClassAccessor accessor(*dex_file_, dex_file_->GetClassDef(class_def_index_));
-
- for (const art::ClassAccessor::Method& method : accessor.GetMethods()) {
- art::CodeItemInstructionAccessor code = method.GetInstructions();
- if (!code.HasCodeItem()) {
- continue;
- }
- uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
- uint32_t offset_end = offset + code.InsnsSizeInBytes();
- uint32_t member_index = method.GetIndex();
- method_cache_[offset_end] = std::make_pair(offset, member_index);
- if (offset <= dex_offset && dex_offset < offset_end) {
- *method_name = dex_file_->PrettyMethod(member_index, false);
- *method_offset = dex_offset - offset;
- return true;
- }
- }
- }
- return false;
+ *method_name = method_info.name;
+ *method_offset = dex_offset - method_info.offset;
+ return true;
}
-bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
+std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
+ const std::string& file) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
- return false;
- }
- struct stat buf;
- if (fstat(fd, &buf) == -1) {
- return false;
- }
- uint64_t length;
- if (buf.st_size < 0 ||
- __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
- static_cast<uint64_t>(buf.st_size) < length) {
- return false;
+ return nullptr;
}
- mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (mapped_memory_ == MAP_FAILED) {
- return false;
- }
- size_ = buf.st_size;
-
- uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);
-
- art::DexFile::Header* header =
- reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
- if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
- !art::CompactDexFile::IsMagicValid(header->magic_)) {
- return false;
- }
-
- if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
- static_cast<uint64_t>(buf.st_size) < length) {
- return false;
- }
-
- art::DexFileLoader loader;
std::string error_msg;
- auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
- false, false, &error_msg);
- dex_file_.reset(dex.release());
- return dex_file_ != nullptr;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+ OpenFromFd(fd, dex_file_offset_in_file, file, &error_msg);
+ if (art_dex_file == nullptr) {
+ return nullptr;
+ }
+
+ return std::unique_ptr<DexFileFromFile>(new DexFileFromFile(std::move(*art_dex_file.release())));
}
-bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
- memory_.resize(sizeof(art::DexFile::Header));
- if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
- return false;
- }
+std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
+ Memory* memory,
+ const std::string& name) {
+ std::vector<uint8_t> backing_memory;
- art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
- uint32_t file_size = header->file_size_;
- if (art::CompactDexFile::IsMagicValid(header->magic_)) {
- // Compact dex file store data section separately so that it can be shared.
- // Therefore we need to extend the read memory range to include it.
- // TODO: This might be wasteful as we might read data in between as well.
- // In practice, this should be fine, as such sharing only happens on disk.
- uint32_t computed_file_size;
- if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
- return false;
+ for (size_t size = 0;;) {
+ std::string error_msg;
+ std::unique_ptr<art_api::dex::DexFile> art_dex_file =
+ OpenFromMemory(backing_memory.data(), &size, name, &error_msg);
+
+ if (art_dex_file != nullptr) {
+ return std::unique_ptr<DexFileFromMemory>(
+ new DexFileFromMemory(std::move(*art_dex_file.release()), std::move(backing_memory)));
}
- if (computed_file_size > file_size) {
- file_size = computed_file_size;
+
+ if (!error_msg.empty()) {
+ return nullptr;
}
- } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
- return false;
+
+ backing_memory.resize(size);
+ if (!memory->ReadFully(dex_file_offset_in_memory, backing_memory.data(),
+ backing_memory.size())) {
+ return nullptr;
+ }
}
-
- memory_.resize(file_size);
- if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
- return false;
- }
-
- header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
-
- art::DexFileLoader loader;
- std::string error_msg;
- auto dex =
- loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
- dex_file_.reset(dex.release());
- return dex_file_ != nullptr;
}
} // namespace unwindstack
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index c123158..ca658e6 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -25,48 +25,41 @@
#include <utility>
#include <vector>
-#include <dex/dex_file-inl.h>
+#include <art_api/dex_file_support.h>
namespace unwindstack {
-class DexFile {
+class DexFile : protected art_api::dex::DexFile {
public:
- DexFile() = default;
virtual ~DexFile() = default;
bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
- static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
+ static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
+ MapInfo* info);
protected:
- void Init();
-
- std::unique_ptr<const art::DexFile> dex_file_;
- std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_; // dex offset to method index.
-
- uint32_t class_def_index_ = 0;
+ DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
};
class DexFileFromFile : public DexFile {
public:
- DexFileFromFile() = default;
- virtual ~DexFileFromFile();
-
- bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
+ static std::unique_ptr<DexFileFromFile> Create(uint64_t dex_file_offset_in_file,
+ const std::string& file);
private:
- void* mapped_memory_ = nullptr;
- size_t size_ = 0;
+ DexFileFromFile(art_api::dex::DexFile&& art_dex_file) : DexFile(std::move(art_dex_file)) {}
};
class DexFileFromMemory : public DexFile {
public:
- DexFileFromMemory() = default;
- virtual ~DexFileFromMemory() = default;
-
- bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
+ static std::unique_ptr<DexFileFromMemory> Create(uint64_t dex_file_offset_in_memory,
+ Memory* memory, const std::string& name);
private:
+ DexFileFromMemory(art_api::dex::DexFile&& art_dex_file, std::vector<uint8_t>&& memory)
+ : DexFile(std::move(art_dex_file)), memory_(std::move(memory)) {}
+
std::vector<uint8_t> memory_;
};
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 451a0b9..63a77e5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -48,11 +48,7 @@
DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
: Global(memory, search_libs) {}
-DexFiles::~DexFiles() {
- for (auto& entry : files_) {
- delete entry.second;
- }
-}
+DexFiles::~DexFiles() {}
void DexFiles::ProcessArch() {
switch (arch()) {
@@ -137,10 +133,11 @@
DexFile* dex_file;
auto entry = files_.find(dex_file_offset);
if (entry == files_.end()) {
- dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
- files_[dex_file_offset] = dex_file;
+ std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
+ dex_file = new_dex_file.get();
+ files_[dex_file_offset] = std::move(new_dex_file);
} else {
- dex_file = entry->second;
+ dex_file = entry->second.get();
}
return dex_file;
}
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 0fa1638..c128b9b 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -204,7 +204,7 @@
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
uint64_t* cur_pc) {
const auto* cfa = &DwarfCfaInfo::kTable[op];
- if (cfa->name == nullptr) {
+ if (cfa->name[0] == '\0') {
log(indent, "Illegal");
log(indent, "Raw Data: 0x%02x", op);
return true;
@@ -260,7 +260,7 @@
}
// Log any of the expression data.
- for (const auto line : expression_lines) {
+ for (const auto& line : expression_lines) {
log(indent + 1, "%s", line.c_str());
}
return true;
@@ -677,29 +677,29 @@
{DW_EH_PE_uleb128, DW_EH_PE_block},
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
},
- {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
- {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x17 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x18 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x19 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x1d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x1f illegal cfa
+ {"", 0, 0, {}, {}}, // 0x20 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x21 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x22 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x23 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x24 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x25 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x26 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x27 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x28 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x29 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
{
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
2,
@@ -714,21 +714,21 @@
{DW_EH_PE_uleb128, DW_EH_PE_uleb128},
{DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
},
- {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa
- {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+ {"", 0, 0, {}, {}}, // 0x31 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x32 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x33 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x34 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x35 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x36 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x37 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x38 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x39 illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3a illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3b illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3c illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3d illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3e illegal cfa
+ {"", 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
};
// Explicitly instantiate DwarfCfa.
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c5ffb8e..569c17c 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -49,7 +49,14 @@
};
struct Info {
- const char* name;
+ // It may seem cleaner to just change the type of 'name' to 'const char *'.
+ // However, having a pointer here would require relocation at runtime,
+ // causing 'kTable' to be placed in data.rel.ro section instead of rodata
+ // section, adding memory pressure to the system. Note that this is only
+ // safe because this is only used in C++ code. C++ standard, unlike C
+ // standard, mandates the array size to be large enough to hold the NULL
+ // terminator when initialized with a string literal.
+ const char name[36];
uint8_t supported_version;
uint8_t num_operands;
uint8_t operands[2];
diff --git a/libunwindstack/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 668527a..802beca 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -61,6 +61,14 @@
table_encoding_ = data[3];
table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
+ // If we can't perform a binary search on the entries, it's not worth
+ // using this object. The calling code will fall back to the DwarfEhFrame
+ // object in this case.
+ if (table_entry_size_ == 0) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
memory_.set_pc_offset(memory_.cur_offset());
if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -137,13 +145,13 @@
}
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
- uint64_t total_entries) {
- CHECK(fde_count_ > 0);
- CHECK(total_entries <= fde_count_);
+bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
+ if (fde_count_ == 0) {
+ return false;
+ }
size_t first = 0;
- size_t last = total_entries;
+ size_t last = fde_count_;
while (first < last) {
size_t current = (first + last) / 2;
const FdeInfo* info = GetFdeInfoFromIndex(current);
@@ -172,87 +180,6 @@
}
template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
- CHECK(fde_count_ != 0);
- last_error_.code = DWARF_ERROR_NONE;
- last_error_.address = 0;
-
- // We can do a binary search if the pc is in the range of the elements
- // that have already been cached.
- if (!fde_info_.empty()) {
- const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
- if (pc >= info->pc) {
- *fde_offset = info->offset;
- return true;
- }
- if (pc < info->pc) {
- return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
- }
- }
-
- if (cur_entries_offset_ == 0) {
- // All entries read, or error encountered.
- return false;
- }
-
- memory_.set_data_offset(entries_data_offset_);
- memory_.set_cur_offset(cur_entries_offset_);
- memory_.set_pc_offset(0);
- cur_entries_offset_ = 0;
-
- FdeInfo* prev_info = nullptr;
- for (size_t current = fde_info_.size();
- current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
- FdeInfo* info = &fde_info_[current];
- uint64_t value;
- if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
- !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
- fde_info_.erase(current);
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = memory_.cur_offset();
- return false;
- }
-
- // Relative encodings require adding in the load bias.
- if (IsEncodingRelative(table_encoding_)) {
- value += load_bias_;
- }
- info->pc = value;
-
- if (pc < info->pc) {
- if (prev_info == nullptr) {
- return false;
- }
- cur_entries_offset_ = memory_.cur_offset();
- *fde_offset = prev_info->offset;
- return true;
- }
- prev_info = info;
- }
-
- if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
- *fde_offset = prev_info->offset;
- return true;
- }
- return false;
-}
-
-template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
- if (fde_count_ == 0) {
- return false;
- }
-
- if (table_entry_size_ > 0) {
- // Do a binary search since the size of each table entry is fixed.
- return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
- } else {
- // Do a sequential search since each table entry size is variable.
- return GetFdeOffsetSequential(pc, fde_offset);
- }
-}
-
-template <typename AddressType>
void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
for (size_t i = 0; i < fde_count_; i++) {
const FdeInfo* info = GetFdeInfoFromIndex(i);
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index e3e9ca8..0e5eef7 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -69,10 +69,6 @@
const FdeInfo* GetFdeInfoFromIndex(size_t index);
- bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset);
-
- bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries);
-
void GetFdes(std::vector<const DwarfFde*>* fdes) override;
protected:
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index 6ffdc0d..b505900 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -104,7 +104,6 @@
bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
CHECK((encoding & 0x0f) == 0);
- CHECK(encoding != DW_EH_PE_aligned);
// Handle the encoding.
switch (encoding) {
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
index 5bc60b9..393eb3e 100644
--- a/libunwindstack/DwarfOp.cpp
+++ b/libunwindstack/DwarfOp.cpp
@@ -32,8 +32,1451 @@
namespace unwindstack {
+enum DwarfOpHandleFunc : uint8_t {
+ OP_ILLEGAL = 0,
+ OP_DEREF,
+ OP_DEREF_SIZE,
+ OP_PUSH,
+ OP_DUP,
+ OP_DROP,
+ OP_OVER,
+ OP_PICK,
+ OP_SWAP,
+ OP_ROT,
+ OP_ABS,
+ OP_AND,
+ OP_DIV,
+ OP_MINUS,
+ OP_MOD,
+ OP_MUL,
+ OP_NEG,
+ OP_NOT,
+ OP_OR,
+ OP_PLUS,
+ OP_PLUS_UCONST,
+ OP_SHL,
+ OP_SHR,
+ OP_SHRA,
+ OP_XOR,
+ OP_BRA,
+ OP_EQ,
+ OP_GE,
+ OP_GT,
+ OP_LE,
+ OP_LT,
+ OP_NE,
+ OP_SKIP,
+ OP_LIT,
+ OP_REG,
+ OP_REGX,
+ OP_BREG,
+ OP_BREGX,
+ OP_NOP,
+ OP_NOT_IMPLEMENTED,
+};
+
+struct OpCallback {
+ // It may seem tempting to "clean this up" by replacing "const char[26]" with
+ // "const char*", but doing so would place the entire callback table in
+ // .data.rel.ro section, instead of .rodata section, and thus increase
+ // dirty memory usage. Libunwindstack is used by the linker and therefore
+ // loaded for every running process, so every bit of memory counts.
+ // Unlike C standard, C++ standard guarantees this array is big enough to
+ // store the names, or else we would get a compilation error.
+ const char name[26];
+
+ // Similarily for this field, we do NOT want to directly store function
+ // pointers here. Not only would that cause the callback table to be placed
+ // in .data.rel.ro section, but it would be duplicated for each AddressType.
+ // Instead, we use DwarfOpHandleFunc enum to decouple the callback table from
+ // the function pointers.
+ DwarfOpHandleFunc handle_func;
+
+ uint8_t num_required_stack_values;
+ uint8_t num_operands;
+ uint8_t operands[2];
+};
+
+constexpr static OpCallback kCallbackTable[256] = {
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x00 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x01 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x02 illegal op
+ {
+ // 0x03 DW_OP_addr
+ "DW_OP_addr",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_absptr},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x04 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x05 illegal op
+ {
+ // 0x06 DW_OP_deref
+ "DW_OP_deref",
+ OP_DEREF,
+ 1,
+ 0,
+ {},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0x07 illegal op
+ {
+ // 0x08 DW_OP_const1u
+ "DW_OP_const1u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x09 DW_OP_const1s
+ "DW_OP_const1s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata1},
+ },
+ {
+ // 0x0a DW_OP_const2u
+ "DW_OP_const2u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x0b DW_OP_const2s
+ "DW_OP_const2s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x0c DW_OP_const4u
+ "DW_OP_const4u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x0d DW_OP_const4s
+ "DW_OP_const4s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata4},
+ },
+ {
+ // 0x0e DW_OP_const8u
+ "DW_OP_const8u",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_udata8},
+ },
+ {
+ // 0x0f DW_OP_const8s
+ "DW_OP_const8s",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sdata8},
+ },
+ {
+ // 0x10 DW_OP_constu
+ "DW_OP_constu",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x11 DW_OP_consts
+ "DW_OP_consts",
+ OP_PUSH,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x12 DW_OP_dup
+ "DW_OP_dup",
+ OP_DUP,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x13 DW_OP_drop
+ "DW_OP_drop",
+ OP_DROP,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x14 DW_OP_over
+ "DW_OP_over",
+ OP_OVER,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x15 DW_OP_pick
+ "DW_OP_pick",
+ OP_PICK,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x16 DW_OP_swap
+ "DW_OP_swap",
+ OP_SWAP,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x17 DW_OP_rot
+ "DW_OP_rot",
+ OP_ROT,
+ 3,
+ 0,
+ {},
+ },
+ {
+ // 0x18 DW_OP_xderef
+ "DW_OP_xderef",
+ OP_NOT_IMPLEMENTED,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x19 DW_OP_abs
+ "DW_OP_abs",
+ OP_ABS,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x1a DW_OP_and
+ "DW_OP_and",
+ OP_AND,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1b DW_OP_div
+ "DW_OP_div",
+ OP_DIV,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1c DW_OP_minus
+ "DW_OP_minus",
+ OP_MINUS,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1d DW_OP_mod
+ "DW_OP_mod",
+ OP_MOD,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1e DW_OP_mul
+ "DW_OP_mul",
+ OP_MUL,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1f DW_OP_neg
+ "DW_OP_neg",
+ OP_NEG,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x20 DW_OP_not
+ "DW_OP_not",
+ OP_NOT,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x21 DW_OP_or
+ "DW_OP_or",
+ OP_OR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x22 DW_OP_plus
+ "DW_OP_plus",
+ OP_PLUS,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x23 DW_OP_plus_uconst
+ "DW_OP_plus_uconst",
+ OP_PLUS_UCONST,
+ 1,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x24 DW_OP_shl
+ "DW_OP_shl",
+ OP_SHL,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x25 DW_OP_shr
+ "DW_OP_shr",
+ OP_SHR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x26 DW_OP_shra
+ "DW_OP_shra",
+ OP_SHRA,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x27 DW_OP_xor
+ "DW_OP_xor",
+ OP_XOR,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x28 DW_OP_bra
+ "DW_OP_bra",
+ OP_BRA,
+ 1,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x29 DW_OP_eq
+ "DW_OP_eq",
+ OP_EQ,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2a DW_OP_ge
+ "DW_OP_ge",
+ OP_GE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2b DW_OP_gt
+ "DW_OP_gt",
+ OP_GT,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2c DW_OP_le
+ "DW_OP_le",
+ OP_LE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2d DW_OP_lt
+ "DW_OP_lt",
+ OP_LT,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2e DW_OP_ne
+ "DW_OP_ne",
+ OP_NE,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2f DW_OP_skip
+ "DW_OP_skip",
+ OP_SKIP,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x30 DW_OP_lit0
+ "DW_OP_lit0",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x31 DW_OP_lit1
+ "DW_OP_lit1",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x32 DW_OP_lit2
+ "DW_OP_lit2",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x33 DW_OP_lit3
+ "DW_OP_lit3",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x34 DW_OP_lit4
+ "DW_OP_lit4",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x35 DW_OP_lit5
+ "DW_OP_lit5",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x36 DW_OP_lit6
+ "DW_OP_lit6",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x37 DW_OP_lit7
+ "DW_OP_lit7",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x38 DW_OP_lit8
+ "DW_OP_lit8",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x39 DW_OP_lit9
+ "DW_OP_lit9",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3a DW_OP_lit10
+ "DW_OP_lit10",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3b DW_OP_lit11
+ "DW_OP_lit11",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3c DW_OP_lit12
+ "DW_OP_lit12",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3d DW_OP_lit13
+ "DW_OP_lit13",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3e DW_OP_lit14
+ "DW_OP_lit14",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3f DW_OP_lit15
+ "DW_OP_lit15",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x40 DW_OP_lit16
+ "DW_OP_lit16",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x41 DW_OP_lit17
+ "DW_OP_lit17",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x42 DW_OP_lit18
+ "DW_OP_lit18",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x43 DW_OP_lit19
+ "DW_OP_lit19",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x44 DW_OP_lit20
+ "DW_OP_lit20",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x45 DW_OP_lit21
+ "DW_OP_lit21",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x46 DW_OP_lit22
+ "DW_OP_lit22",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x47 DW_OP_lit23
+ "DW_OP_lit23",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x48 DW_OP_lit24
+ "DW_OP_lit24",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x49 DW_OP_lit25
+ "DW_OP_lit25",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4a DW_OP_lit26
+ "DW_OP_lit26",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4b DW_OP_lit27
+ "DW_OP_lit27",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4c DW_OP_lit28
+ "DW_OP_lit28",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4d DW_OP_lit29
+ "DW_OP_lit29",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4e DW_OP_lit30
+ "DW_OP_lit30",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4f DW_OP_lit31
+ "DW_OP_lit31",
+ OP_LIT,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x50 DW_OP_reg0
+ "DW_OP_reg0",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x51 DW_OP_reg1
+ "DW_OP_reg1",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x52 DW_OP_reg2
+ "DW_OP_reg2",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x53 DW_OP_reg3
+ "DW_OP_reg3",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x54 DW_OP_reg4
+ "DW_OP_reg4",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x55 DW_OP_reg5
+ "DW_OP_reg5",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x56 DW_OP_reg6
+ "DW_OP_reg6",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x57 DW_OP_reg7
+ "DW_OP_reg7",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x58 DW_OP_reg8
+ "DW_OP_reg8",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x59 DW_OP_reg9
+ "DW_OP_reg9",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5a DW_OP_reg10
+ "DW_OP_reg10",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5b DW_OP_reg11
+ "DW_OP_reg11",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5c DW_OP_reg12
+ "DW_OP_reg12",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5d DW_OP_reg13
+ "DW_OP_reg13",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5e DW_OP_reg14
+ "DW_OP_reg14",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5f DW_OP_reg15
+ "DW_OP_reg15",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x60 DW_OP_reg16
+ "DW_OP_reg16",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x61 DW_OP_reg17
+ "DW_OP_reg17",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x62 DW_OP_reg18
+ "DW_OP_reg18",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x63 DW_OP_reg19
+ "DW_OP_reg19",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x64 DW_OP_reg20
+ "DW_OP_reg20",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x65 DW_OP_reg21
+ "DW_OP_reg21",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x66 DW_OP_reg22
+ "DW_OP_reg22",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x67 DW_OP_reg23
+ "DW_OP_reg23",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x68 DW_OP_reg24
+ "DW_OP_reg24",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x69 DW_OP_reg25
+ "DW_OP_reg25",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6a DW_OP_reg26
+ "DW_OP_reg26",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6b DW_OP_reg27
+ "DW_OP_reg27",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6c DW_OP_reg28
+ "DW_OP_reg28",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6d DW_OP_reg29
+ "DW_OP_reg29",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6e DW_OP_reg30
+ "DW_OP_reg30",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6f DW_OP_reg31
+ "DW_OP_reg31",
+ OP_REG,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x70 DW_OP_breg0
+ "DW_OP_breg0",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x71 DW_OP_breg1
+ "DW_OP_breg1",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x72 DW_OP_breg2
+ "DW_OP_breg2",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x73 DW_OP_breg3
+ "DW_OP_breg3",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x74 DW_OP_breg4
+ "DW_OP_breg4",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x75 DW_OP_breg5
+ "DW_OP_breg5",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x76 DW_OP_breg6
+ "DW_OP_breg6",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x77 DW_OP_breg7
+ "DW_OP_breg7",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x78 DW_OP_breg8
+ "DW_OP_breg8",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x79 DW_OP_breg9
+ "DW_OP_breg9",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7a DW_OP_breg10
+ "DW_OP_breg10",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7b DW_OP_breg11
+ "DW_OP_breg11",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7c DW_OP_breg12
+ "DW_OP_breg12",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7d DW_OP_breg13
+ "DW_OP_breg13",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7e DW_OP_breg14
+ "DW_OP_breg14",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7f DW_OP_breg15
+ "DW_OP_breg15",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x80 DW_OP_breg16
+ "DW_OP_breg16",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x81 DW_OP_breg17
+ "DW_OP_breg17",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x82 DW_OP_breg18
+ "DW_OP_breg18",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x83 DW_OP_breg19
+ "DW_OP_breg19",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x84 DW_OP_breg20
+ "DW_OP_breg20",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x85 DW_OP_breg21
+ "DW_OP_breg21",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x86 DW_OP_breg22
+ "DW_OP_breg22",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x87 DW_OP_breg23
+ "DW_OP_breg23",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x88 DW_OP_breg24
+ "DW_OP_breg24",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x89 DW_OP_breg25
+ "DW_OP_breg25",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8a DW_OP_breg26
+ "DW_OP_breg26",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8b DW_OP_breg27
+ "DW_OP_breg27",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8c DW_OP_breg28
+ "DW_OP_breg28",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8d DW_OP_breg29
+ "DW_OP_breg29",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8e DW_OP_breg30
+ "DW_OP_breg30",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8f DW_OP_breg31
+ "DW_OP_breg31",
+ OP_BREG,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x90 DW_OP_regx
+ "DW_OP_regx",
+ OP_REGX,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x91 DW_OP_fbreg
+ "DW_OP_fbreg",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x92 DW_OP_bregx
+ "DW_OP_bregx",
+ OP_BREGX,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ },
+ {
+ // 0x93 DW_OP_piece
+ "DW_OP_piece",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x94 DW_OP_deref_size
+ "DW_OP_deref_size",
+ OP_DEREF_SIZE,
+ 1,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x95 DW_OP_xderef_size
+ "DW_OP_xderef_size",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x96 DW_OP_nop
+ "DW_OP_nop",
+ OP_NOP,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x97 DW_OP_push_object_address
+ "DW_OP_push_object_address",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x98 DW_OP_call2
+ "DW_OP_call2",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x99 DW_OP_call4
+ "DW_OP_call4",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x9a DW_OP_call_ref
+ "DW_OP_call_ref",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0, // Has a different sized operand (4 bytes or 8 bytes).
+ {},
+ },
+ {
+ // 0x9b DW_OP_form_tls_address
+ "DW_OP_form_tls_address",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9c DW_OP_call_frame_cfa
+ "DW_OP_call_frame_cfa",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9d DW_OP_bit_piece
+ "DW_OP_bit_piece",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9e DW_OP_implicit_value
+ "DW_OP_implicit_value",
+ OP_NOT_IMPLEMENTED,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9f DW_OP_stack_value
+ "DW_OP_stack_value",
+ OP_NOT_IMPLEMENTED,
+ 1,
+ 0,
+ {},
+ },
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xa9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xaa illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xab illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xac illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xad illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xae illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xaf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xb9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xba illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbe illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xbf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xc9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xca illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xce illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xcf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xd9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xda illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xde illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xdf illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe0 DW_OP_lo_user
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xe9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xea illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xeb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xec illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xed illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xee illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xef illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf0 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf1 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf2 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf3 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf4 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf5 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf6 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf7 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf8 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xf9 illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfa illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfb illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfc illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfd illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xfe illegal op
+ {"", OP_ILLEGAL, 0, 0, {}}, // 0xff DW_OP_hi_user
+};
+
template <typename AddressType>
-constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+const typename DwarfOp<AddressType>::OpHandleFuncPtr DwarfOp<AddressType>::kOpHandleFuncList[] = {
+ [OP_ILLEGAL] = nullptr,
+ [OP_DEREF] = &DwarfOp<AddressType>::op_deref,
+ [OP_DEREF_SIZE] = &DwarfOp<AddressType>::op_deref_size,
+ [OP_PUSH] = &DwarfOp<AddressType>::op_push,
+ [OP_DUP] = &DwarfOp<AddressType>::op_dup,
+ [OP_DROP] = &DwarfOp<AddressType>::op_drop,
+ [OP_OVER] = &DwarfOp<AddressType>::op_over,
+ [OP_PICK] = &DwarfOp<AddressType>::op_pick,
+ [OP_SWAP] = &DwarfOp<AddressType>::op_swap,
+ [OP_ROT] = &DwarfOp<AddressType>::op_rot,
+ [OP_ABS] = &DwarfOp<AddressType>::op_abs,
+ [OP_AND] = &DwarfOp<AddressType>::op_and,
+ [OP_DIV] = &DwarfOp<AddressType>::op_div,
+ [OP_MINUS] = &DwarfOp<AddressType>::op_minus,
+ [OP_MOD] = &DwarfOp<AddressType>::op_mod,
+ [OP_MUL] = &DwarfOp<AddressType>::op_mul,
+ [OP_NEG] = &DwarfOp<AddressType>::op_neg,
+ [OP_NOT] = &DwarfOp<AddressType>::op_not,
+ [OP_OR] = &DwarfOp<AddressType>::op_or,
+ [OP_PLUS] = &DwarfOp<AddressType>::op_plus,
+ [OP_PLUS_UCONST] = &DwarfOp<AddressType>::op_plus_uconst,
+ [OP_SHL] = &DwarfOp<AddressType>::op_shl,
+ [OP_SHR] = &DwarfOp<AddressType>::op_shr,
+ [OP_SHRA] = &DwarfOp<AddressType>::op_shra,
+ [OP_XOR] = &DwarfOp<AddressType>::op_xor,
+ [OP_BRA] = &DwarfOp<AddressType>::op_bra,
+ [OP_EQ] = &DwarfOp<AddressType>::op_eq,
+ [OP_GE] = &DwarfOp<AddressType>::op_ge,
+ [OP_GT] = &DwarfOp<AddressType>::op_gt,
+ [OP_LE] = &DwarfOp<AddressType>::op_le,
+ [OP_LT] = &DwarfOp<AddressType>::op_lt,
+ [OP_NE] = &DwarfOp<AddressType>::op_ne,
+ [OP_SKIP] = &DwarfOp<AddressType>::op_skip,
+ [OP_LIT] = &DwarfOp<AddressType>::op_lit,
+ [OP_REG] = &DwarfOp<AddressType>::op_reg,
+ [OP_REGX] = &DwarfOp<AddressType>::op_regx,
+ [OP_BREG] = &DwarfOp<AddressType>::op_breg,
+ [OP_BREGX] = &DwarfOp<AddressType>::op_bregx,
+ [OP_NOP] = &DwarfOp<AddressType>::op_nop,
+ [OP_NOT_IMPLEMENTED] = &DwarfOp<AddressType>::op_not_implemented,
+};
template <typename AddressType>
bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
@@ -97,12 +1540,13 @@
}
const auto* op = &kCallbackTable[cur_op_];
- const auto handle_func = op->handle_func;
- if (handle_func == nullptr) {
+ if (op->handle_func == OP_ILLEGAL) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
+ const auto handle_func = kOpHandleFuncList[op->handle_func];
+
// Make sure that the required number of stack elements is available.
if (stack_.size() < op->num_required_stack_values) {
last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -135,7 +1579,7 @@
std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
std::string log_string;
const auto* op = &kCallbackTable[cur_op];
- if (op->handle_func == nullptr) {
+ if (op->handle_func == OP_ILLEGAL) {
log_string = "Illegal";
} else {
log_string = op->name;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index 4c69b3d..ac9fd2d 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -42,14 +42,6 @@
// Signed version of AddressType
typedef typename std::make_signed<AddressType>::type SignedType;
- struct OpCallback {
- const char* name;
- bool (DwarfOp::*handle_func)();
- uint8_t num_required_stack_values;
- uint8_t num_operands;
- uint8_t operands[2];
- };
-
public:
DwarfOp(DwarfMemory* memory, Memory* regular_memory)
: memory_(memory), regular_memory_(regular_memory) {}
@@ -143,1342 +135,8 @@
bool op_nop();
bool op_not_implemented();
- constexpr static OpCallback kCallbackTable[256] = {
- {nullptr, nullptr, 0, 0, {}}, // 0x00 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0x01 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0x02 illegal op
- {
- // 0x03 DW_OP_addr
- "DW_OP_addr",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_absptr},
- },
- {nullptr, nullptr, 0, 0, {}}, // 0x04 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0x05 illegal op
- {
- // 0x06 DW_OP_deref
- "DW_OP_deref",
- &DwarfOp::op_deref,
- 1,
- 0,
- {},
- },
- {nullptr, nullptr, 0, 0, {}}, // 0x07 illegal op
- {
- // 0x08 DW_OP_const1u
- "DW_OP_const1u",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_udata1},
- },
- {
- // 0x09 DW_OP_const1s
- "DW_OP_const1s",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_sdata1},
- },
- {
- // 0x0a DW_OP_const2u
- "DW_OP_const2u",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_udata2},
- },
- {
- // 0x0b DW_OP_const2s
- "DW_OP_const2s",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_sdata2},
- },
- {
- // 0x0c DW_OP_const4u
- "DW_OP_const4u",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_udata4},
- },
- {
- // 0x0d DW_OP_const4s
- "DW_OP_const4s",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_sdata4},
- },
- {
- // 0x0e DW_OP_const8u
- "DW_OP_const8u",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_udata8},
- },
- {
- // 0x0f DW_OP_const8s
- "DW_OP_const8s",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_sdata8},
- },
- {
- // 0x10 DW_OP_constu
- "DW_OP_constu",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_uleb128},
- },
- {
- // 0x11 DW_OP_consts
- "DW_OP_consts",
- &DwarfOp::op_push,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x12 DW_OP_dup
- "DW_OP_dup",
- &DwarfOp::op_dup,
- 1,
- 0,
- {},
- },
- {
- // 0x13 DW_OP_drop
- "DW_OP_drop",
- &DwarfOp::op_drop,
- 1,
- 0,
- {},
- },
- {
- // 0x14 DW_OP_over
- "DW_OP_over",
- &DwarfOp::op_over,
- 2,
- 0,
- {},
- },
- {
- // 0x15 DW_OP_pick
- "DW_OP_pick",
- &DwarfOp::op_pick,
- 0,
- 1,
- {DW_EH_PE_udata1},
- },
- {
- // 0x16 DW_OP_swap
- "DW_OP_swap",
- &DwarfOp::op_swap,
- 2,
- 0,
- {},
- },
- {
- // 0x17 DW_OP_rot
- "DW_OP_rot",
- &DwarfOp::op_rot,
- 3,
- 0,
- {},
- },
- {
- // 0x18 DW_OP_xderef
- "DW_OP_xderef",
- &DwarfOp::op_not_implemented,
- 2,
- 0,
- {},
- },
- {
- // 0x19 DW_OP_abs
- "DW_OP_abs",
- &DwarfOp::op_abs,
- 1,
- 0,
- {},
- },
- {
- // 0x1a DW_OP_and
- "DW_OP_and",
- &DwarfOp::op_and,
- 2,
- 0,
- {},
- },
- {
- // 0x1b DW_OP_div
- "DW_OP_div",
- &DwarfOp::op_div,
- 2,
- 0,
- {},
- },
- {
- // 0x1c DW_OP_minus
- "DW_OP_minus",
- &DwarfOp::op_minus,
- 2,
- 0,
- {},
- },
- {
- // 0x1d DW_OP_mod
- "DW_OP_mod",
- &DwarfOp::op_mod,
- 2,
- 0,
- {},
- },
- {
- // 0x1e DW_OP_mul
- "DW_OP_mul",
- &DwarfOp::op_mul,
- 2,
- 0,
- {},
- },
- {
- // 0x1f DW_OP_neg
- "DW_OP_neg",
- &DwarfOp::op_neg,
- 1,
- 0,
- {},
- },
- {
- // 0x20 DW_OP_not
- "DW_OP_not",
- &DwarfOp::op_not,
- 1,
- 0,
- {},
- },
- {
- // 0x21 DW_OP_or
- "DW_OP_or",
- &DwarfOp::op_or,
- 2,
- 0,
- {},
- },
- {
- // 0x22 DW_OP_plus
- "DW_OP_plus",
- &DwarfOp::op_plus,
- 2,
- 0,
- {},
- },
- {
- // 0x23 DW_OP_plus_uconst
- "DW_OP_plus_uconst",
- &DwarfOp::op_plus_uconst,
- 1,
- 1,
- {DW_EH_PE_uleb128},
- },
- {
- // 0x24 DW_OP_shl
- "DW_OP_shl",
- &DwarfOp::op_shl,
- 2,
- 0,
- {},
- },
- {
- // 0x25 DW_OP_shr
- "DW_OP_shr",
- &DwarfOp::op_shr,
- 2,
- 0,
- {},
- },
- {
- // 0x26 DW_OP_shra
- "DW_OP_shra",
- &DwarfOp::op_shra,
- 2,
- 0,
- {},
- },
- {
- // 0x27 DW_OP_xor
- "DW_OP_xor",
- &DwarfOp::op_xor,
- 2,
- 0,
- {},
- },
- {
- // 0x28 DW_OP_bra
- "DW_OP_bra",
- &DwarfOp::op_bra,
- 1,
- 1,
- {DW_EH_PE_sdata2},
- },
- {
- // 0x29 DW_OP_eq
- "DW_OP_eq",
- &DwarfOp::op_eq,
- 2,
- 0,
- {},
- },
- {
- // 0x2a DW_OP_ge
- "DW_OP_ge",
- &DwarfOp::op_ge,
- 2,
- 0,
- {},
- },
- {
- // 0x2b DW_OP_gt
- "DW_OP_gt",
- &DwarfOp::op_gt,
- 2,
- 0,
- {},
- },
- {
- // 0x2c DW_OP_le
- "DW_OP_le",
- &DwarfOp::op_le,
- 2,
- 0,
- {},
- },
- {
- // 0x2d DW_OP_lt
- "DW_OP_lt",
- &DwarfOp::op_lt,
- 2,
- 0,
- {},
- },
- {
- // 0x2e DW_OP_ne
- "DW_OP_ne",
- &DwarfOp::op_ne,
- 2,
- 0,
- {},
- },
- {
- // 0x2f DW_OP_skip
- "DW_OP_skip",
- &DwarfOp::op_skip,
- 0,
- 1,
- {DW_EH_PE_sdata2},
- },
- {
- // 0x30 DW_OP_lit0
- "DW_OP_lit0",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x31 DW_OP_lit1
- "DW_OP_lit1",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x32 DW_OP_lit2
- "DW_OP_lit2",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x33 DW_OP_lit3
- "DW_OP_lit3",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x34 DW_OP_lit4
- "DW_OP_lit4",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x35 DW_OP_lit5
- "DW_OP_lit5",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x36 DW_OP_lit6
- "DW_OP_lit6",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x37 DW_OP_lit7
- "DW_OP_lit7",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x38 DW_OP_lit8
- "DW_OP_lit8",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x39 DW_OP_lit9
- "DW_OP_lit9",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3a DW_OP_lit10
- "DW_OP_lit10",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3b DW_OP_lit11
- "DW_OP_lit11",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3c DW_OP_lit12
- "DW_OP_lit12",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3d DW_OP_lit13
- "DW_OP_lit13",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3e DW_OP_lit14
- "DW_OP_lit14",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x3f DW_OP_lit15
- "DW_OP_lit15",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x40 DW_OP_lit16
- "DW_OP_lit16",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x41 DW_OP_lit17
- "DW_OP_lit17",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x42 DW_OP_lit18
- "DW_OP_lit18",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x43 DW_OP_lit19
- "DW_OP_lit19",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x44 DW_OP_lit20
- "DW_OP_lit20",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x45 DW_OP_lit21
- "DW_OP_lit21",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x46 DW_OP_lit22
- "DW_OP_lit22",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x47 DW_OP_lit23
- "DW_OP_lit23",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x48 DW_OP_lit24
- "DW_OP_lit24",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x49 DW_OP_lit25
- "DW_OP_lit25",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4a DW_OP_lit26
- "DW_OP_lit26",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4b DW_OP_lit27
- "DW_OP_lit27",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4c DW_OP_lit28
- "DW_OP_lit28",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4d DW_OP_lit29
- "DW_OP_lit29",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4e DW_OP_lit30
- "DW_OP_lit30",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x4f DW_OP_lit31
- "DW_OP_lit31",
- &DwarfOp::op_lit,
- 0,
- 0,
- {},
- },
- {
- // 0x50 DW_OP_reg0
- "DW_OP_reg0",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x51 DW_OP_reg1
- "DW_OP_reg1",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x52 DW_OP_reg2
- "DW_OP_reg2",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x53 DW_OP_reg3
- "DW_OP_reg3",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x54 DW_OP_reg4
- "DW_OP_reg4",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x55 DW_OP_reg5
- "DW_OP_reg5",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x56 DW_OP_reg6
- "DW_OP_reg6",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x57 DW_OP_reg7
- "DW_OP_reg7",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x58 DW_OP_reg8
- "DW_OP_reg8",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x59 DW_OP_reg9
- "DW_OP_reg9",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5a DW_OP_reg10
- "DW_OP_reg10",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5b DW_OP_reg11
- "DW_OP_reg11",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5c DW_OP_reg12
- "DW_OP_reg12",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5d DW_OP_reg13
- "DW_OP_reg13",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5e DW_OP_reg14
- "DW_OP_reg14",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x5f DW_OP_reg15
- "DW_OP_reg15",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x60 DW_OP_reg16
- "DW_OP_reg16",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x61 DW_OP_reg17
- "DW_OP_reg17",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x62 DW_OP_reg18
- "DW_OP_reg18",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x63 DW_OP_reg19
- "DW_OP_reg19",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x64 DW_OP_reg20
- "DW_OP_reg20",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x65 DW_OP_reg21
- "DW_OP_reg21",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x66 DW_OP_reg22
- "DW_OP_reg22",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x67 DW_OP_reg23
- "DW_OP_reg23",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x68 DW_OP_reg24
- "DW_OP_reg24",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x69 DW_OP_reg25
- "DW_OP_reg25",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6a DW_OP_reg26
- "DW_OP_reg26",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6b DW_OP_reg27
- "DW_OP_reg27",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6c DW_OP_reg28
- "DW_OP_reg28",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6d DW_OP_reg29
- "DW_OP_reg29",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6e DW_OP_reg30
- "DW_OP_reg30",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x6f DW_OP_reg31
- "DW_OP_reg31",
- &DwarfOp::op_reg,
- 0,
- 0,
- {},
- },
- {
- // 0x70 DW_OP_breg0
- "DW_OP_breg0",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x71 DW_OP_breg1
- "DW_OP_breg1",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x72 DW_OP_breg2
- "DW_OP_breg2",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x73 DW_OP_breg3
- "DW_OP_breg3",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x74 DW_OP_breg4
- "DW_OP_breg4",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x75 DW_OP_breg5
- "DW_OP_breg5",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x76 DW_OP_breg6
- "DW_OP_breg6",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x77 DW_OP_breg7
- "DW_OP_breg7",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x78 DW_OP_breg8
- "DW_OP_breg8",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x79 DW_OP_breg9
- "DW_OP_breg9",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7a DW_OP_breg10
- "DW_OP_breg10",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7b DW_OP_breg11
- "DW_OP_breg11",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7c DW_OP_breg12
- "DW_OP_breg12",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7d DW_OP_breg13
- "DW_OP_breg13",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7e DW_OP_breg14
- "DW_OP_breg14",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x7f DW_OP_breg15
- "DW_OP_breg15",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x80 DW_OP_breg16
- "DW_OP_breg16",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x81 DW_OP_breg17
- "DW_OP_breg17",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x82 DW_OP_breg18
- "DW_OP_breg18",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x83 DW_OP_breg19
- "DW_OP_breg19",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x84 DW_OP_breg20
- "DW_OP_breg20",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x85 DW_OP_breg21
- "DW_OP_breg21",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x86 DW_OP_breg22
- "DW_OP_breg22",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x87 DW_OP_breg23
- "DW_OP_breg23",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x88 DW_OP_breg24
- "DW_OP_breg24",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x89 DW_OP_breg25
- "DW_OP_breg25",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8a DW_OP_breg26
- "DW_OP_breg26",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8b DW_OP_breg27
- "DW_OP_breg27",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8c DW_OP_breg28
- "DW_OP_breg28",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8d DW_OP_breg29
- "DW_OP_breg29",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8e DW_OP_breg30
- "DW_OP_breg30",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x8f DW_OP_breg31
- "DW_OP_breg31",
- &DwarfOp::op_breg,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x90 DW_OP_regx
- "DW_OP_regx",
- &DwarfOp::op_regx,
- 0,
- 1,
- {DW_EH_PE_uleb128},
- },
- {
- // 0x91 DW_OP_fbreg
- "DW_OP_fbreg",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_sleb128},
- },
- {
- // 0x92 DW_OP_bregx
- "DW_OP_bregx",
- &DwarfOp::op_bregx,
- 0,
- 2,
- {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
- },
- {
- // 0x93 DW_OP_piece
- "DW_OP_piece",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_uleb128},
- },
- {
- // 0x94 DW_OP_deref_size
- "DW_OP_deref_size",
- &DwarfOp::op_deref_size,
- 1,
- 1,
- {DW_EH_PE_udata1},
- },
- {
- // 0x95 DW_OP_xderef_size
- "DW_OP_xderef_size",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_udata1},
- },
- {
- // 0x96 DW_OP_nop
- "DW_OP_nop",
- &DwarfOp::op_nop,
- 0,
- 0,
- {},
- },
- {
- // 0x97 DW_OP_push_object_address
- "DW_OP_push_object_address",
- &DwarfOp::op_not_implemented,
- 0,
- 0,
- {},
- },
- {
- // 0x98 DW_OP_call2
- "DW_OP_call2",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_udata2},
- },
- {
- // 0x99 DW_OP_call4
- "DW_OP_call4",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_udata4},
- },
- {
- // 0x9a DW_OP_call_ref
- "DW_OP_call_ref",
- &DwarfOp::op_not_implemented,
- 0,
- 0, // Has a different sized operand (4 bytes or 8 bytes).
- {},
- },
- {
- // 0x9b DW_OP_form_tls_address
- "DW_OP_form_tls_address",
- &DwarfOp::op_not_implemented,
- 0,
- 0,
- {},
- },
- {
- // 0x9c DW_OP_call_frame_cfa
- "DW_OP_call_frame_cfa",
- &DwarfOp::op_not_implemented,
- 0,
- 0,
- {},
- },
- {
- // 0x9d DW_OP_bit_piece
- "DW_OP_bit_piece",
- &DwarfOp::op_not_implemented,
- 0,
- 2,
- {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
- },
- {
- // 0x9e DW_OP_implicit_value
- "DW_OP_implicit_value",
- &DwarfOp::op_not_implemented,
- 0,
- 1,
- {DW_EH_PE_uleb128},
- },
- {
- // 0x9f DW_OP_stack_value
- "DW_OP_stack_value",
- &DwarfOp::op_not_implemented,
- 1,
- 0,
- {},
- },
- {nullptr, nullptr, 0, 0, {}}, // 0xa0 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xa9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xaa illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xab illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xac illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xad illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xae illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xaf illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb0 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xb9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xba illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xbb illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xbc illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xbd illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xbe illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xbf illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc0 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xc9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xca illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xcb illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xcc illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xcd illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xce illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xcf illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd0 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xd9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xda illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xdb illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xdc illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xdd illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xde illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xdf illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe0 DW_OP_lo_user
- {nullptr, nullptr, 0, 0, {}}, // 0xe1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xe9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xea illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xeb illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xec illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xed illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xee illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xef illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf0 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf1 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf2 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf3 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf4 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf5 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf6 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf7 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf8 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xf9 illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xfa illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xfb illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xfc illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xfd illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xfe illegal op
- {nullptr, nullptr, 0, 0, {}}, // 0xff DW_OP_hi_user
- };
+ using OpHandleFuncPtr = bool (DwarfOp::*)();
+ static const OpHandleFuncPtr kOpHandleFuncList[];
};
} // namespace unwindstack
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 57a780e..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -138,7 +138,7 @@
return false;
}
- if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
// Unrecognized version.
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
return false;
@@ -155,7 +155,7 @@
cie->augmentation_string.push_back(aug_value);
} while (aug_value != '\0');
- if (cie->version == 4) {
+ if (cie->version == 4 || cie->version == 5) {
// Skip the Address Size field since we only use it for validation.
memory_.set_cur_offset(memory_.cur_offset() + 1);
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4d72ead..3454913 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -93,9 +93,12 @@
valid_ = false;
}
-bool Elf::GetSoname(std::string* name) {
+std::string Elf::GetSoname() {
std::lock_guard<std::mutex> guard(lock_);
- return valid_ && interface_->GetSoname(name);
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetSoname();
}
uint64_t Elf::GetRelPc(uint64_t pc, const MapInfo* map_info) {
@@ -140,6 +143,13 @@
return true;
}
+std::string Elf::GetBuildID() {
+ if (!valid_) {
+ return "";
+ }
+ return interface_->GetBuildID();
+}
+
void Elf::GetLastError(ErrorData* data) {
if (valid_) {
*data = interface_->last_error();
@@ -150,7 +160,7 @@
if (valid_) {
return interface_->LastErrorCode();
}
- return ERROR_NONE;
+ return ERROR_INVALID_ELF;
}
uint64_t Elf::GetLastErrorAddress() {
@@ -160,22 +170,23 @@
return 0;
}
+// The relative pc expectd by this function is relative to the start of the elf.
+bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
+ if (!valid_) {
+ return false;
+ }
+ return regs->StepIfSignalHandler(rel_pc, this, process_memory);
+}
+
// The relative pc is always relative to the start of the map from which it comes.
-bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
- bool* finished) {
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
if (!valid_) {
return false;
}
- // The relative pc expectd by StepIfSignalHandler is relative to the start of the elf.
- if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
- *finished = false;
- return true;
- }
-
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
- return interface_->Step(adjusted_rel_pc, regs, process_memory, finished);
+ return interface_->Step(rel_pc, regs, process_memory, finished);
}
bool Elf::IsValidElf(Memory* memory) {
@@ -380,4 +391,22 @@
return false;
}
+std::string Elf::GetBuildID(Memory* memory) {
+ if (!IsValidElf(memory)) {
+ return "";
+ }
+
+ uint8_t class_type;
+ if (!memory->Read(EI_CLASS, &class_type, 1)) {
+ return "";
+ }
+
+ if (class_type == ELFCLASS32) {
+ return ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(memory);
+ } else if (class_type == ELFCLASS64) {
+ return ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(memory);
+ }
+ return "";
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index f59a472..bdfee01 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -29,12 +29,12 @@
#include <unwindstack/DwarfSection.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include "DwarfDebugFrame.h"
#include "DwarfEhFrame.h"
#include "DwarfEhFrameWithHdr.h"
+#include "MemoryBuffer.h"
#include "Symbols.h"
namespace unwindstack {
@@ -177,14 +177,14 @@
template <typename EhdrType, typename PhdrType>
uint64_t ElfInterface::GetLoadBias(Memory* memory) {
EhdrType ehdr;
- if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
return false;
}
uint64_t offset = ehdr.e_phoff;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
- if (!memory->Read(offset, &phdr, sizeof(phdr))) {
+ if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
return 0;
}
if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
@@ -197,6 +197,7 @@
template <typename EhdrType, typename PhdrType>
void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
uint64_t offset = ehdr.e_phoff;
+ bool first_exec_load_header = true;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
@@ -212,9 +213,11 @@
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
static_cast<size_t>(phdr.p_memsz)};
- if (phdr.p_offset == 0) {
- *load_bias = phdr.p_vaddr;
+ // Only set the load bias from the first executable load header.
+ if (first_exec_load_header && phdr.p_vaddr > phdr.p_offset) {
+ *load_bias = phdr.p_vaddr - phdr.p_offset;
}
+ first_exec_load_header = false;
break;
}
@@ -237,6 +240,58 @@
}
}
+template <typename NhdrType>
+std::string ElfInterface::ReadBuildID() {
+ // Ensure there is no overflow in any of the calulations below.
+ uint64_t tmp;
+ if (__builtin_add_overflow(gnu_build_id_offset_, gnu_build_id_size_, &tmp)) {
+ return "";
+ }
+
+ uint64_t offset = 0;
+ while (offset < gnu_build_id_size_) {
+ if (gnu_build_id_size_ - offset < sizeof(NhdrType)) {
+ return "";
+ }
+ NhdrType hdr;
+ if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &hdr, sizeof(hdr))) {
+ return "";
+ }
+ offset += sizeof(hdr);
+
+ if (gnu_build_id_size_ - offset < hdr.n_namesz) {
+ return "";
+ }
+ if (hdr.n_namesz > 0) {
+ std::string name(hdr.n_namesz, '\0');
+ if (!memory_->ReadFully(gnu_build_id_offset_ + offset, &(name[0]), hdr.n_namesz)) {
+ return "";
+ }
+
+ // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+ if (name.back() == '\0')
+ name.resize(name.size() - 1);
+
+ // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_namesz + 3) & ~3;
+
+ if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+ if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
+ }
+ std::string build_id(hdr.n_descsz, '\0');
+ if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
+ }
+ }
+ // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_descsz + 3) & ~3;
+ }
+ return "";
+}
+
template <typename EhdrType, typename ShdrType>
void ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
uint64_t offset = ehdr.e_shoff;
@@ -258,7 +313,7 @@
// Skip the first header, it's always going to be NULL.
offset += ehdr.e_shentsize;
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
- if (!memory_->Read(offset, &shdr, sizeof(shdr))) {
+ if (!memory_->ReadFully(offset, &shdr, sizeof(shdr))) {
return;
}
@@ -270,7 +325,7 @@
continue;
}
uint64_t str_offset = ehdr.e_shoff + shdr.sh_link * ehdr.e_shentsize;
- if (!memory_->Read(str_offset, &str_shdr, sizeof(str_shdr))) {
+ if (!memory_->ReadFully(str_offset, &str_shdr, sizeof(str_shdr))) {
continue;
}
if (str_shdr.sh_type != SHT_STRTAB) {
@@ -308,18 +363,26 @@
// In order to read soname, keep track of address to offset mapping.
strtabs_.push_back(std::make_pair<uint64_t, uint64_t>(static_cast<uint64_t>(shdr.sh_addr),
static_cast<uint64_t>(shdr.sh_offset)));
+ } else if (shdr.sh_type == SHT_NOTE) {
+ if (shdr.sh_name < sec_size) {
+ std::string name;
+ if (memory_->ReadString(sec_offset + shdr.sh_name, &name) &&
+ name == ".note.gnu.build-id") {
+ gnu_build_id_offset_ = shdr.sh_offset;
+ gnu_build_id_size_ = shdr.sh_size;
+ }
+ }
}
}
}
template <typename DynType>
-bool ElfInterface::GetSonameWithTemplate(std::string* soname) {
+std::string ElfInterface::GetSonameWithTemplate() {
if (soname_type_ == SONAME_INVALID) {
- return false;
+ return "";
}
if (soname_type_ == SONAME_VALID) {
- *soname = soname_;
- return true;
+ return soname_;
}
soname_type_ = SONAME_INVALID;
@@ -336,7 +399,7 @@
if (!memory_->ReadFully(offset, &dyn, sizeof(dyn))) {
last_error_.code = ERROR_MEMORY_INVALID;
last_error_.address = offset;
- return false;
+ return "";
}
if (dyn.d_tag == DT_STRTAB) {
@@ -355,17 +418,16 @@
if (entry.first == strtab_addr) {
soname_offset = entry.second + soname_offset;
if (soname_offset >= entry.second + strtab_size) {
- return false;
+ return "";
}
if (!memory_->ReadString(soname_offset, &soname_)) {
- return false;
+ return "";
}
soname_type_ = SONAME_VALID;
- *soname = soname_;
- return true;
+ return soname_;
}
}
- return false;
+ return "";
}
template <typename SymType>
@@ -477,6 +539,103 @@
*size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
}
+template <typename EhdrType, typename ShdrType>
+bool GetBuildIDInfo(Memory* memory, uint64_t* build_id_offset, uint64_t* build_id_size) {
+ EhdrType ehdr;
+ if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
+ return false;
+ }
+
+ uint64_t offset = ehdr.e_shoff;
+ uint64_t sec_offset;
+ uint64_t sec_size;
+ ShdrType shdr;
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ return false;
+ }
+
+ uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
+ if (!memory->ReadFully(sh_offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ sec_offset = shdr.sh_offset;
+ sec_size = shdr.sh_size;
+
+ // Skip the first header, it's always going to be NULL.
+ offset += ehdr.e_shentsize;
+ for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
+ if (!memory->ReadFully(offset, &shdr, sizeof(shdr))) {
+ return false;
+ }
+ std::string name;
+ if (shdr.sh_type == SHT_NOTE && shdr.sh_name < sec_size &&
+ memory->ReadString(sec_offset + shdr.sh_name, &name) && name == ".note.gnu.build-id") {
+ *build_id_offset = shdr.sh_offset;
+ *build_id_size = shdr.sh_size;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <typename EhdrType, typename ShdrType, typename NhdrType>
+std::string ElfInterface::ReadBuildIDFromMemory(Memory* memory) {
+ uint64_t note_offset;
+ uint64_t note_size;
+ if (!GetBuildIDInfo<EhdrType, ShdrType>(memory, ¬e_offset, ¬e_size)) {
+ return "";
+ }
+
+ // Ensure there is no overflow in any of the calculations below.
+ uint64_t tmp;
+ if (__builtin_add_overflow(note_offset, note_size, &tmp)) {
+ return "";
+ }
+
+ uint64_t offset = 0;
+ while (offset < note_size) {
+ if (note_size - offset < sizeof(NhdrType)) {
+ return "";
+ }
+ NhdrType hdr;
+ if (!memory->ReadFully(note_offset + offset, &hdr, sizeof(hdr))) {
+ return "";
+ }
+ offset += sizeof(hdr);
+
+ if (note_size - offset < hdr.n_namesz) {
+ return "";
+ }
+ if (hdr.n_namesz > 0) {
+ std::string name(hdr.n_namesz, '\0');
+ if (!memory->ReadFully(note_offset + offset, &(name[0]), hdr.n_namesz)) {
+ return "";
+ }
+
+ // Trim trailing \0 as GNU is stored as a C string in the ELF file.
+ if (name.back() == '\0') name.resize(name.size() - 1);
+
+ // Align hdr.n_namesz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_namesz + 3) & ~3;
+
+ if (name == "GNU" && hdr.n_type == NT_GNU_BUILD_ID) {
+ if (note_size - offset < hdr.n_descsz || hdr.n_descsz == 0) {
+ return "";
+ }
+ std::string build_id(hdr.n_descsz - 1, '\0');
+ if (memory->ReadFully(note_offset + offset, &build_id[0], hdr.n_descsz)) {
+ return build_id;
+ }
+ return "";
+ }
+ }
+ // Align hdr.n_descsz to next power multiple of 4. See man 5 elf.
+ offset += (hdr.n_descsz + 3) & ~3;
+ }
+ return "";
+}
+
// Instantiate all of the needed template functions.
template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
@@ -492,8 +651,11 @@
template void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
-template bool ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(std::string*);
-template bool ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(std::string*);
+template std::string ElfInterface::ReadBuildID<Elf32_Nhdr>();
+template std::string ElfInterface::ReadBuildID<Elf64_Nhdr>();
+
+template std::string ElfInterface::GetSonameWithTemplate<Elf32_Dyn>();
+template std::string ElfInterface::GetSonameWithTemplate<Elf64_Dyn>();
template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, std::string*,
uint64_t*);
@@ -509,4 +671,9 @@
template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr>(
+ Memory*);
+template std::string ElfInterface::ReadBuildIDFromMemory<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr>(
+ Memory*);
+
} // namespace unwindstack
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index 7a3de01..a20be00 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <sys/mman.h>
#include <string>
@@ -76,7 +77,7 @@
// f0000-f2000 0 r-- /system/lib/libc.so
// f2000-f3000 2000 rw- /system/lib/libc.so
MapInfo* map_start = nullptr;
- for (MapInfo* info : *maps) {
+ for (const auto& info : *maps) {
if (map_start != nullptr) {
if (map_start->name == info->name) {
if (info->offset != 0 &&
@@ -95,7 +96,7 @@
}
if (map_start == nullptr && (info->flags & PROT_READ) && info->offset == 0 &&
!info->name.empty()) {
- map_start = info;
+ map_start = info.get();
}
}
}
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 20bc4b9..8a85607 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -23,7 +23,8 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
// This implements the JIT Compilation Interface.
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 5b2fadf..5d81200 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -111,6 +111,14 @@
pc_adjustment = 0;
}
step_pc -= pc_adjustment;
+
+ bool finished = false;
+ if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
+ step_pc = rel_pc;
+ } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished)) {
+ finished = true;
+ }
+
// Skip any locations that are within this library.
if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
// Add frame information.
@@ -124,22 +132,12 @@
}
num_frames++;
}
- if (!elf->valid()) {
- break;
- }
- if (frame_info->size() == max_frames) {
- break;
- }
+ if (finished || frame_info->size() == max_frames ||
+ (cur_pc == regs->pc() && cur_sp == regs->sp())) {
+ break;
+ }
adjust_pc = true;
- bool finished;
- if (!elf->Step(rel_pc, step_pc, regs.get(), process_memory_.get(), &finished) || finished) {
- break;
- }
- // pc and sp are the same, terminate the unwind.
- if (cur_pc == regs->pc() && cur_sp == regs->sp()) {
- break;
- }
}
return num_frames != 0;
}
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index fe32b5e..5b30a4d 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -22,13 +22,43 @@
#include <mutex>
#include <string>
+#include <android-base/stringprintf.h>
+
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryFileAtOffset.h"
+#include "MemoryRange.h"
namespace unwindstack {
+bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
+ // One last attempt, see if the previous map is read-only with the
+ // same name and stretches across this map.
+ if (prev_map == nullptr || prev_map->flags != PROT_READ) {
+ return false;
+ }
+
+ uint64_t map_size = end - prev_map->end;
+ if (!memory->Init(name, prev_map->offset, map_size)) {
+ return false;
+ }
+
+ uint64_t max_size;
+ if (!Elf::GetInfo(memory, &max_size) || max_size < map_size) {
+ return false;
+ }
+
+ if (!memory->Init(name, prev_map->offset, max_size)) {
+ return false;
+ }
+
+ elf_offset = offset - prev_map->offset;
+ elf_start_offset = prev_map->offset;
+ return true;
+}
+
Memory* MapInfo::GetFileMemory() {
std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
if (offset == 0) {
@@ -38,8 +68,12 @@
return nullptr;
}
- // There are two possibilities when the offset is non-zero.
- // - There is an elf file embedded in a file.
+ // These are the possibilities when the offset is non-zero.
+ // - There is an elf file embedded in a file, and the offset is the
+ // the start of the elf in the file.
+ // - There is an elf file embedded in a file, and the offset is the
+ // the start of the executable part of the file. The actual start
+ // of the elf is in the read-only segment preceeding this map.
// - The whole file is an elf file, and the offset needs to be saved.
//
// Map in just the part of the file for the map. If this is not
@@ -53,27 +87,49 @@
return nullptr;
}
- uint64_t max_size;
- if (!Elf::GetInfo(memory.get(), &max_size)) {
- // Init as if the whole file is an elf.
- if (memory->Init(name, 0)) {
- elf_offset = offset;
- return memory.release();
+ // Check if the start of this map is an embedded elf.
+ uint64_t max_size = 0;
+ if (Elf::GetInfo(memory.get(), &max_size)) {
+ elf_start_offset = offset;
+ if (max_size > map_size) {
+ if (memory->Init(name, offset, max_size)) {
+ return memory.release();
+ }
+ // Try to reinit using the default map_size.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ elf_start_offset = 0;
+ return nullptr;
}
- return nullptr;
+ return memory.release();
}
- if (max_size > map_size) {
- if (memory->Init(name, offset, max_size)) {
- return memory.release();
+ // No elf at offset, try to init as if the whole file is an elf.
+ if (memory->Init(name, 0) && Elf::IsValidElf(memory.get())) {
+ elf_offset = offset;
+ // Need to check how to set the elf start offset. If this map is not
+ // the r-x map of a r-- map, then use the real offset value. Otherwise,
+ // use 0.
+ if (prev_map == nullptr || prev_map->offset != 0 || prev_map->flags != PROT_READ ||
+ prev_map->name != name) {
+ elf_start_offset = offset;
}
- // Try to reinit using the default map_size.
- if (memory->Init(name, offset, map_size)) {
- return memory.release();
- }
- return nullptr;
+ return memory.release();
}
- return memory.release();
+
+ // See if the map previous to this one contains a read-only map
+ // that represents the real start of the elf data.
+ if (InitFileMemoryFromPreviousReadOnlyMap(memory.get())) {
+ return memory.release();
+ }
+
+ // Failed to find elf at start of file or at read-only map, return
+ // file object from the current map.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ return nullptr;
}
Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
@@ -96,8 +152,7 @@
}
}
- // If the map isn't readable, don't bother trying to read from process memory.
- if (!(flags & PROT_READ)) {
+ if (process_memory == nullptr) {
return nullptr;
}
@@ -108,86 +163,105 @@
// option is used.
std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start, end - start, 0));
if (Elf::IsValidElf(memory.get())) {
+ memory_backed_elf = true;
return memory.release();
}
- if (name.empty() || maps_ == nullptr) {
+ // Find the read-only map by looking at the previous map. The linker
+ // doesn't guarantee that this invariant will always be true. However,
+ // if that changes, there is likely something else that will change and
+ // break something.
+ if (offset == 0 || name.empty() || prev_map == nullptr || prev_map->name != name ||
+ prev_map->offset >= offset) {
return nullptr;
}
- // Find the read-only map that has the same name and has an offset closest
- // to the current offset but less than the offset of the current map.
- // For shared libraries, there should be a r-x map that has a non-zero
- // offset and then a r-- map that has a zero offset.
- // For shared libraries loaded from an apk, there should be a r-x map that
- // has a non-zero offset and then a r-- map that has a non-zero offset less
- // than the offset from the r-x map.
- uint64_t closest_offset = 0;
- MapInfo* ro_map_info = nullptr;
- for (auto map_info : *maps_) {
- if (map_info->flags == PROT_READ && map_info->name == name && map_info->offset < offset &&
- map_info->offset >= closest_offset) {
- ro_map_info = map_info;
- closest_offset = ro_map_info->offset;
- }
- }
+ // Make sure that relative pc values are corrected properly.
+ elf_offset = offset - prev_map->offset;
+ // Use this as the elf start offset, otherwise, you always get offsets into
+ // the r-x section, which is not quite the right information.
+ elf_start_offset = prev_map->offset;
- if (ro_map_info != nullptr) {
- // Make sure that relative pc values are corrected properly.
- elf_offset = offset - closest_offset;
+ MemoryRanges* ranges = new MemoryRanges;
+ ranges->Insert(
+ new MemoryRange(process_memory, prev_map->start, prev_map->end - prev_map->start, 0));
+ ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
- MemoryRanges* ranges = new MemoryRanges;
- ranges->Insert(new MemoryRange(process_memory, ro_map_info->start,
- ro_map_info->end - ro_map_info->start, 0));
- ranges->Insert(new MemoryRange(process_memory, start, end - start, elf_offset));
-
- return ranges;
- }
- return nullptr;
+ memory_backed_elf = true;
+ return ranges;
}
Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
- // Make sure no other thread is trying to add the elf to this map.
- std::lock_guard<std::mutex> guard(mutex_);
+ {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(mutex_);
- if (elf.get() != nullptr) {
- return elf.get();
- }
-
- bool locked = false;
- if (Elf::CachingEnabled() && !name.empty()) {
- Elf::CacheLock();
- locked = true;
- if (Elf::CacheGet(this)) {
- Elf::CacheUnlock();
+ if (elf.get() != nullptr) {
return elf.get();
}
+
+ bool locked = false;
+ if (Elf::CachingEnabled() && !name.empty()) {
+ Elf::CacheLock();
+ locked = true;
+ if (Elf::CacheGet(this)) {
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+
+ Memory* memory = CreateMemory(process_memory);
+ if (locked) {
+ if (Elf::CacheAfterCreateMemory(this)) {
+ delete memory;
+ Elf::CacheUnlock();
+ return elf.get();
+ }
+ }
+ elf.reset(new Elf(memory));
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ elf->Init();
+ if (elf->valid() && expected_arch != elf->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf->Invalidate();
+ }
+
+ if (locked) {
+ Elf::CacheAdd(this);
+ Elf::CacheUnlock();
+ }
}
- Memory* memory = CreateMemory(process_memory);
- if (locked) {
- if (Elf::CacheAfterCreateMemory(this)) {
- delete memory;
- Elf::CacheUnlock();
- return elf.get();
+ if (!elf->valid()) {
+ elf_start_offset = offset;
+ } else if (prev_map != nullptr && elf_start_offset != offset &&
+ prev_map->offset == elf_start_offset && prev_map->name == name) {
+ // If there is a read-only map then a read-execute map that represents the
+ // same elf object, make sure the previous map is using the same elf
+ // object if it hasn't already been set.
+ std::lock_guard<std::mutex> guard(prev_map->mutex_);
+ if (prev_map->elf.get() == nullptr) {
+ prev_map->elf = elf;
+ prev_map->memory_backed_elf = memory_backed_elf;
}
}
- elf.reset(new Elf(memory));
- // If the init fails, keep the elf around as an invalid object so we
- // don't try to reinit the object.
- elf->Init();
- if (elf->valid() && expected_arch != elf->arch()) {
- // Make the elf invalid, mismatch between arch and expected arch.
- elf->Invalidate();
- }
-
- if (locked) {
- Elf::CacheAdd(this);
- Elf::CacheUnlock();
- }
return elf.get();
}
+bool MapInfo::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) {
+ {
+ // Make sure no other thread is trying to update this elf object.
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (elf == nullptr) {
+ return false;
+ }
+ }
+ // No longer need the lock, once the elf object is created, it is not deleted
+ // until this object is deleted.
+ return elf->GetFunctionName(addr, name, func_offset);
+}
+
uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
uint64_t cur_load_bias = load_bias.load();
if (cur_load_bias != static_cast<uint64_t>(-1)) {
@@ -217,4 +291,61 @@
return cur_load_bias;
}
+MapInfo::~MapInfo() {
+ uintptr_t id = build_id.load();
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ }
+}
+
+std::string MapInfo::GetBuildID() {
+ uintptr_t id = build_id.load();
+ if (id != 0) {
+ return *reinterpret_cast<std::string*>(id);
+ }
+
+ // No need to lock, at worst if multiple threads do this at the same
+ // time it should be detected and only one thread should win and
+ // save the data.
+ std::unique_ptr<std::string> cur_build_id(new std::string);
+
+ // Now need to see if the elf object exists.
+ // Make sure no other thread is trying to add the elf to this map.
+ mutex_.lock();
+ Elf* elf_obj = elf.get();
+ mutex_.unlock();
+ if (elf_obj != nullptr) {
+ *cur_build_id = elf_obj->GetBuildID();
+ } else {
+ // This will only work if we can get the file associated with this memory.
+ // If this is only available in memory, then the section name information
+ // is not present and we will not be able to find the build id info.
+ std::unique_ptr<Memory> memory(GetFileMemory());
+ if (memory != nullptr) {
+ *cur_build_id = Elf::GetBuildID(memory.get());
+ }
+ }
+
+ id = reinterpret_cast<uintptr_t>(cur_build_id.get());
+ uintptr_t expected_id = 0;
+ if (build_id.compare_exchange_weak(expected_id, id)) {
+ // Value saved, so make sure the memory is not freed.
+ cur_build_id.release();
+ }
+ return *reinterpret_cast<std::string*>(id);
+}
+
+std::string MapInfo::GetPrintableBuildID() {
+ std::string raw_build_id = GetBuildID();
+ if (raw_build_id.empty()) {
+ return "";
+ }
+ std::string printable_build_id;
+ for (const char& c : raw_build_id) {
+ // Use %hhx to avoid sign extension on abis that have signed chars.
+ printable_build_id += android::base::StringPrintf("%02hhx", c);
+ }
+ return printable_build_id;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 8729871..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@@ -46,9 +47,9 @@
size_t last = maps_.size();
while (first < last) {
size_t index = (first + last) / 2;
- MapInfo* cur = maps_[index];
+ const auto& cur = maps_[index];
if (pc >= cur->start && pc < cur->end) {
- return cur;
+ return cur.get();
} else if (pc < cur->start) {
last = index;
} else {
@@ -61,30 +62,36 @@
bool Maps::Parse() {
return android::procinfo::ReadMapFile(
GetMapsFile(),
- [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+ maps_.emplace_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+ flags, name));
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name, uint64_t load_bias) {
- MapInfo* map_info = new MapInfo(this, start, end, offset, flags, name);
+ auto map_info =
+ std::make_unique<MapInfo>(maps_.empty() ? nullptr : maps_.back().get(), start, end, offset,
+ flags, name);
map_info->load_bias = load_bias;
- maps_.push_back(map_info);
+ maps_.emplace_back(std::move(map_info));
}
void Maps::Sort() {
std::sort(maps_.begin(), maps_.end(),
- [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
-}
+ [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+ return a->start < b->start; });
-Maps::~Maps() {
- for (auto& map : maps_) {
- delete map;
+ // Set the prev_map values on the info objects.
+ MapInfo* prev_map = nullptr;
+ for (const auto& map_info : maps_) {
+ map_info->prev_map = prev_map;
+ prev_map = map_info.get();
}
}
@@ -92,12 +99,14 @@
std::string content(buffer_);
return android::procinfo::ReadMapFileContent(
&content[0],
- [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, const char* name) {
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t, const char* name) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
if (strncmp(name, "/dev/", 5) == 0 && strncmp(name + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.push_back(new MapInfo(this, start, end, pgoff, flags, name));
+ maps_.emplace_back(
+ new MapInfo(maps_.empty() ? nullptr : maps_.back().get(), start, end, pgoff,
+ flags, name));
});
}
@@ -113,10 +122,6 @@
// New maps will be added at the end without deleting the old ones.
size_t last_map_idx = maps_.size();
if (!Parse()) {
- // Delete any maps added by the Parse call.
- for (size_t i = last_map_idx; i < maps_.size(); i++) {
- delete maps_[i];
- }
maps_.resize(last_map_idx);
return false;
}
@@ -124,17 +129,16 @@
size_t total_entries = maps_.size();
size_t search_map_idx = 0;
for (size_t new_map_idx = last_map_idx; new_map_idx < maps_.size(); new_map_idx++) {
- MapInfo* new_map_info = maps_[new_map_idx];
+ auto& new_map_info = maps_[new_map_idx];
uint64_t start = new_map_info->start;
uint64_t end = new_map_info->end;
uint64_t flags = new_map_info->flags;
std::string* name = &new_map_info->name;
for (size_t old_map_idx = search_map_idx; old_map_idx < last_map_idx; old_map_idx++) {
- MapInfo* info = maps_[old_map_idx];
+ auto& info = maps_[old_map_idx];
if (start == info->start && end == info->end && flags == info->flags && *name == info->name) {
// No need to check
search_map_idx = old_map_idx + 1;
- delete new_map_info;
maps_[new_map_idx] = nullptr;
total_entries--;
break;
@@ -147,7 +151,7 @@
// Never delete these maps, they may be in use. The assumption is
// that there will only every be a handfull of these so waiting
// to destroy them is not too expensive.
- saved_maps_.push_back(info);
+ saved_maps_.emplace_back(std::move(info));
maps_[old_map_idx] = nullptr;
total_entries--;
}
@@ -158,14 +162,14 @@
// Now move out any of the maps that never were found.
for (size_t i = search_map_idx; i < last_map_idx; i++) {
- saved_maps_.push_back(maps_[i]);
+ saved_maps_.emplace_back(std::move(maps_[i]));
maps_[i] = nullptr;
total_entries--;
}
// Sort all of the values such that the nullptrs wind up at the end, then
// resize them away.
- std::sort(maps_.begin(), maps_.end(), [](const auto* a, const auto* b) {
+ std::sort(maps_.begin(), maps_.end(), [](const auto& a, const auto& b) {
if (a == nullptr) {
return false;
} else if (b == nullptr) {
@@ -178,10 +182,4 @@
return true;
}
-LocalUpdatableMaps::~LocalUpdatableMaps() {
- for (auto map_info : saved_maps_) {
- delete map_info;
- }
-}
-
} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index cfa8c6d..a66cd5b 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -16,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <string.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
@@ -31,6 +32,14 @@
#include <unwindstack/Memory.h>
#include "Check.h"
+#include "MemoryBuffer.h"
+#include "MemoryCache.h"
+#include "MemoryFileAtOffset.h"
+#include "MemoryLocal.h"
+#include "MemoryOffline.h"
+#include "MemoryOfflineBuffer.h"
+#include "MemoryRange.h"
+#include "MemoryRemote.h"
namespace unwindstack {
@@ -167,6 +176,16 @@
return false;
}
+std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
+ auto memory = std::make_unique<MemoryFileAtOffset>();
+
+ if (memory->Init(path, offset)) {
+ return memory;
+ }
+
+ return nullptr;
+}
+
std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
if (pid == getpid()) {
return std::shared_ptr<Memory>(new MemoryLocal());
@@ -174,6 +193,18 @@
return std::shared_ptr<Memory>(new MemoryRemote(pid));
}
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+ }
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
+std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end) {
+ return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
+}
+
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
if (addr >= raw_.size()) {
return 0;
@@ -398,4 +429,50 @@
return 0;
}
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+ // Only bother caching and looking at the cache if this is a small read for now.
+ if (size > 64) {
+ return impl_->Read(addr, dst, size);
+ }
+
+ uint64_t addr_page = addr >> kCacheBits;
+ auto entry = cache_.find(addr_page);
+ uint8_t* cache_dst;
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr, dst, size);
+ }
+ }
+ size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+ if (size <= max_read) {
+ memcpy(dst, &cache_dst[addr & kCacheMask], size);
+ return size;
+ }
+
+ // The read crossed into another cached entry, since a read can only cross
+ // into one extra cached page, duplicate the code rather than looping.
+ memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+ dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+ addr_page++;
+
+ entry = cache_.find(addr_page);
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+ }
+ }
+ memcpy(dst, cache_dst, size - max_read);
+ return size;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
new file mode 100644
index 0000000..3fe4bbb
--- /dev/null
+++ b/libunwindstack/MemoryBuffer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryBuffer : public Memory {
+ public:
+ MemoryBuffer() = default;
+ virtual ~MemoryBuffer() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint8_t* GetPtr(size_t offset);
+
+ void Resize(size_t size) { raw_.resize(size); }
+
+ uint64_t Size() { return raw_.size(); }
+
+ private:
+ std::vector<uint8_t> raw_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
new file mode 100644
index 0000000..769d907
--- /dev/null
+++ b/libunwindstack/MemoryCache.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
+#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryCache : public Memory {
+ public:
+ MemoryCache(Memory* memory) : impl_(memory) {}
+ virtual ~MemoryCache() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ void Clear() override { cache_.clear(); }
+
+ private:
+ constexpr static size_t kCacheBits = 12;
+ constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+ constexpr static size_t kCacheSize = 1 << kCacheBits;
+ std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+ std::unique_ptr<Memory> impl_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
new file mode 100644
index 0000000..d136eb4
--- /dev/null
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryFileAtOffset : public Memory {
+ public:
+ MemoryFileAtOffset() = default;
+ virtual ~MemoryFileAtOffset();
+
+ bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ size_t Size() { return size_; }
+
+ void Clear() override;
+
+ protected:
+ size_t size_ = 0;
+ size_t offset_ = 0;
+ uint8_t* data_ = nullptr;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
new file mode 100644
index 0000000..29aaf12
--- /dev/null
+++ b/libunwindstack/MemoryLocal.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
+#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryLocal : public Memory {
+ public:
+ MemoryLocal() = default;
+ virtual ~MemoryLocal() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
new file mode 100644
index 0000000..789f1a2
--- /dev/null
+++ b/libunwindstack/MemoryOffline.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryRange.h"
+
+namespace unwindstack {
+
+class MemoryOffline : public Memory {
+ public:
+ MemoryOffline() = default;
+ virtual ~MemoryOffline() = default;
+
+ bool Init(const std::string& file, uint64_t offset);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::unique_ptr<MemoryRange> memory_;
+};
+
+class MemoryOfflineParts : public Memory {
+ public:
+ MemoryOfflineParts() = default;
+ virtual ~MemoryOfflineParts();
+
+ void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::vector<MemoryOffline*> memories_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
new file mode 100644
index 0000000..64c49a1
--- /dev/null
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+
+#include <stdint.h>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryOfflineBuffer : public Memory {
+ public:
+ MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
+ virtual ~MemoryOfflineBuffer() = default;
+
+ void Reset(const uint8_t* data, uint64_t start, uint64_t end);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ const uint8_t* data_;
+ uint64_t start_;
+ uint64_t end_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
new file mode 100644
index 0000000..3b4ab5c
--- /dev/null
+++ b/libunwindstack/MemoryRange.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_RANGE_H
+#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// MemoryRange maps one address range onto another.
+// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
+// such that range.read(offset) is equivalent to underlying.read(src_begin).
+class MemoryRange : public Memory {
+ public:
+ MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
+ uint64_t offset);
+ virtual ~MemoryRange() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ uint64_t offset() { return offset_; }
+ uint64_t length() { return length_; }
+
+ private:
+ std::shared_ptr<Memory> memory_;
+ uint64_t begin_;
+ uint64_t length_;
+ uint64_t offset_;
+};
+
+class MemoryRanges : public Memory {
+ public:
+ MemoryRanges() = default;
+ virtual ~MemoryRanges() = default;
+
+ void Insert(MemoryRange* memory);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
new file mode 100644
index 0000000..db367d6
--- /dev/null
+++ b/libunwindstack/MemoryRemote.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_MEMORY_REMOTE_H
+#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <atomic>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+class MemoryRemote : public Memory {
+ public:
+ MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
+ virtual ~MemoryRemote() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ pid_t pid() { return pid_; }
+
+ private:
+ pid_t pid_;
+ std::atomic_uintptr_t read_redirect_func_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index de22bde..885dc94 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <functional>
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index a68f6e0..e9787aa 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <functional>
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 2e6908c..f330fe0 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <functional>
@@ -134,7 +135,7 @@
Memory* elf_memory = elf->memory();
// Read from elf memory since it is usually more expensive to read from
// process memory.
- if (!elf_memory->Read(rel_pc, &data, sizeof(data))) {
+ if (!elf_memory->ReadFully(rel_pc, &data, sizeof(data))) {
return false;
}
@@ -159,7 +160,7 @@
// read sc_pc and sc_regs[32] from stack
uint64_t values[MIPS_REG_LAST];
- if (!process_memory->Read(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
+ if (!process_memory->ReadFully(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
return false;
}
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 0b835a1..3f67d92 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <functional>
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index ebad3f4..74cd1cb 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <string.h>
#include <functional>
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index 14ebdbb..e3c15a2 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -17,6 +17,7 @@
#include <elf.h>
#include <stdint.h>
+#include <algorithm>
#include <string>
#include <unwindstack/Memory.h>
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index b3c5549..7556482 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -25,17 +25,22 @@
#include <algorithm>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
#if !defined(NO_LIBDEXFILE_SUPPORT)
#include <unwindstack/DexFiles.h>
#endif
+// Use the demangler from libc++.
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
+
namespace unwindstack {
// Inject extra 'virtual' frame that represents the dex pc data.
@@ -59,7 +64,11 @@
if (info != nullptr) {
frame->map_start = info->start;
frame->map_end = info->end;
- frame->map_offset = info->offset;
+ // Since this is a dex file frame, the elf_start_offset is not set
+ // by any of the normal code paths. Use the offset of the map since
+ // that matches the actual offset.
+ frame->map_elf_start_offset = info->offset;
+ frame->map_exact_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
if (resolve_names_) {
@@ -85,8 +94,8 @@
#endif
}
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
- uint64_t pc_adjustment) {
+FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+ uint64_t pc_adjustment) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
@@ -96,23 +105,26 @@
frame->pc = regs_->pc() - pc_adjustment;
if (map_info == nullptr) {
- return;
+ // Nothing else to update.
+ return nullptr;
}
if (resolve_names_) {
frame->map_name = map_info->name;
+ if (embedded_soname_ && map_info->elf_start_offset != 0 && !frame->map_name.empty()) {
+ std::string soname = elf->GetSoname();
+ if (!soname.empty()) {
+ frame->map_name += '!' + soname;
+ }
+ }
}
- frame->map_offset = map_info->offset;
+ frame->map_elf_start_offset = map_info->elf_start_offset;
+ frame->map_exact_offset = map_info->offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
frame->map_flags = map_info->flags;
frame->map_load_bias = elf->GetLoadBias();
-
- if (!resolve_names_ ||
- !elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
- frame->function_name = "";
- frame->function_offset = 0;
- }
+ return frame;
}
static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
@@ -134,12 +146,12 @@
frames_.clear();
last_error_.code = ERROR_NONE;
last_error_.address = 0;
+ elf_from_memory_not_file_ = false;
ArchEnum arch = regs_->Arch();
bool return_address_attempt = false;
bool adjust_pc = false;
- std::unique_ptr<JitDebug> jit_debug;
for (; frames_.size() < max_frames_;) {
uint64_t cur_pc = regs_->pc();
uint64_t cur_sp = regs_->sp();
@@ -158,6 +170,12 @@
break;
}
elf = map_info->GetElf(process_memory_, arch);
+ // If this elf is memory backed, and there is a valid file, then set
+ // an indicator that we couldn't open the file.
+ if (!elf_from_memory_not_file_ && map_info->memory_backed_elf && !map_info->name.empty() &&
+ map_info->name[0] != '[' && !android::base::StartsWith(map_info->name, "/memfd:")) {
+ elf_from_memory_not_file_ = true;
+ }
step_pc = regs_->pc();
rel_pc = elf->GetRelPc(step_pc, map_info);
// Everyone except elf data in gdb jit debug maps uses the relative pc.
@@ -184,6 +202,7 @@
}
}
+ FrameData* frame = nullptr;
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
@@ -200,23 +219,21 @@
}
}
- FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
+ frame = FillInFrame(map_info, elf, rel_pc, pc_adjustment);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
}
adjust_pc = true;
- bool stepped;
+ bool stepped = false;
bool in_device_map = false;
- if (map_info == nullptr) {
- stepped = false;
- } else {
+ bool finished = false;
+ if (map_info != nullptr) {
if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
- stepped = false;
in_device_map = true;
} else {
MapInfo* sp_info = maps_->Find(regs_->sp());
@@ -224,23 +241,47 @@
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
// some of the speculative frames.
- stepped = false;
in_device_map = true;
} else {
- bool finished;
- stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
- elf->GetLastError(&last_error_);
- if (stepped && finished) {
- break;
+ if (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
+ stepped = true;
+ if (frame != nullptr) {
+ // Need to adjust the relative pc because the signal handler
+ // pc should not be adjusted.
+ frame->rel_pc = rel_pc;
+ frame->pc += pc_adjustment;
+ step_pc = rel_pc;
+ }
+ } else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished)) {
+ stepped = true;
}
+ elf->GetLastError(&last_error_);
}
}
}
+ if (frame != nullptr) {
+ if (!resolve_names_ ||
+ !elf->GetFunctionName(step_pc, &frame->function_name, &frame->function_offset)) {
+ frame->function_name = "";
+ frame->function_offset = 0;
+ }
+ }
+
+ if (finished) {
+ break;
+ }
+
if (!stepped) {
if (return_address_attempt) {
- // Remove the speculative frame.
- frames_.pop_back();
+ // Only remove the speculative frame if there are more than two frames
+ // or the pc in the first frame is in a valid map.
+ // This allows for a case where the code jumps into the middle of
+ // nowhere, but there is no other unwind information after that.
+ if (frames_.size() > 2 || (frames_.size() > 0 && maps_->Find(frames_[0].pc) != nullptr)) {
+ // Remove the speculative frame.
+ frames_.pop_back();
+ }
break;
} else if (in_device_map) {
// Do not attempt any other unwinding, pc or sp is in a device
@@ -268,26 +309,14 @@
}
}
-std::string Unwinder::FormatFrame(size_t frame_num) {
- if (frame_num >= frames_.size()) {
- return "";
- }
- return FormatFrame(frames_[frame_num], regs_->Is32Bit());
-}
-
-std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
+std::string Unwinder::FormatFrame(const FrameData& frame) {
std::string data;
-
- if (is32bit) {
+ if (regs_->Is32Bit()) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
}
- if (frame.map_offset != 0) {
- data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
- }
-
if (frame.map_start == frame.map_end) {
// No valid map associated with this frame.
data += " <unknown>";
@@ -296,16 +325,43 @@
} else {
data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
}
+
+ if (frame.map_elf_start_offset != 0) {
+ data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+ }
+
if (!frame.function_name.empty()) {
- data += " (" + frame.function_name;
+ char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+ if (demangled_name == nullptr) {
+ data += " (" + frame.function_name;
+ } else {
+ data += " (";
+ data += demangled_name;
+ free(demangled_name);
+ }
if (frame.function_offset != 0) {
data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
}
data += ')';
}
+
+ MapInfo* map_info = maps_->Find(frame.map_start);
+ if (map_info != nullptr && display_build_id_) {
+ std::string build_id = map_info->GetPrintableBuildID();
+ if (!build_id.empty()) {
+ data += " (BuildId: " + build_id + ')';
+ }
+ }
return data;
}
+std::string Unwinder::FormatFrame(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrame(frames_[frame_num]);
+}
+
void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
jit_debug->SetArch(arch);
jit_debug_ = jit_debug;
@@ -318,4 +374,29 @@
}
#endif
+bool UnwinderFromPid::Init(ArchEnum arch) {
+ if (pid_ == getpid()) {
+ maps_ptr_.reset(new LocalMaps());
+ } else {
+ maps_ptr_.reset(new RemoteMaps(pid_));
+ }
+ if (!maps_ptr_->Parse()) {
+ return false;
+ }
+ maps_ = maps_ptr_.get();
+
+ process_memory_ = Memory::CreateProcessMemoryCached(pid_);
+
+ jit_debug_ptr_.reset(new JitDebug(process_memory_));
+ jit_debug_ = jit_debug_ptr_.get();
+ SetJitDebug(jit_debug_, arch);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ dex_files_ptr_.reset(new DexFiles(process_memory_));
+ dex_files_ = dex_files_ptr_.get();
+ SetDexFiles(dex_files_, arch);
+#endif
+
+ return true;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 0000000..de9137a
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <android-base/strings.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+ unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+ unwinder.Unwind();
+ return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_cached_unwind);
+
+static void Initialize(benchmark::State& state, unwindstack::Maps& maps,
+ unwindstack::MapInfo** build_id_map_info) {
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ return;
+ }
+
+ // Find the libc.so share library and use that for benchmark purposes.
+ *build_id_map_info = nullptr;
+ for (auto& map_info : maps) {
+ if (map_info->offset == 0 && map_info->GetBuildID() != "") {
+ *build_id_map_info = map_info.get();
+ break;
+ }
+ }
+
+ if (*build_id_map_info == nullptr) {
+ state.SkipWithError("Failed to find a map with a BuildID.");
+ }
+}
+
+static void BM_get_build_id_from_elf(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ Initialize(state, maps, &build_id_map_info);
+
+ unwindstack::Elf* elf = build_id_map_info->GetElf(std::shared_ptr<unwindstack::Memory>(),
+ unwindstack::Regs::CurrentArch());
+ if (!elf->valid()) {
+ state.SkipWithError("Cannot get valid elf from map.");
+ }
+
+ for (auto _ : state) {
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_get_build_id_from_elf);
+
+static void BM_get_build_id_from_file(benchmark::State& state) {
+ unwindstack::LocalMaps maps;
+ unwindstack::MapInfo* build_id_map_info;
+ Initialize(state, maps, &build_id_map_info);
+
+ for (auto _ : state) {
+ uintptr_t id = build_id_map_info->build_id;
+ if (id != 0) {
+ delete reinterpret_cast<std::string*>(id);
+ build_id_map_info->build_id = 0;
+ }
+ benchmark::DoNotOptimize(build_id_map_info->GetBuildID());
+ }
+}
+BENCHMARK(BM_get_build_id_from_file);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index c202a33..67a9640 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -40,7 +40,7 @@
public:
explicit DexFiles(std::shared_ptr<Memory>& memory);
DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
- ~DexFiles();
+ virtual ~DexFiles();
DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
@@ -66,7 +66,7 @@
std::mutex lock_;
bool initialized_ = false;
- std::unordered_map<uint64_t, DexFile*> files_;
+ std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
uint64_t entry_addr_ = 0;
uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index e5b0a89..56bf318 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,7 +59,7 @@
void Invalidate();
- bool GetSoname(std::string* name);
+ std::string GetSoname();
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -67,11 +67,14 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
- bool* finished);
+ bool StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
+ std::string GetBuildID();
+
uint64_t GetLoadBias() { return load_bias_; }
bool IsValidPc(uint64_t pc);
@@ -100,6 +103,8 @@
static uint64_t GetLoadBias(Memory* memory);
+ static std::string GetBuildID(Memory* memory);
+
static void SetCachingEnabled(bool enable);
static bool CachingEnabled() { return cache_enabled_; }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index a45eba8..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -56,12 +56,14 @@
virtual void InitHeaders(uint64_t load_bias) = 0;
- virtual bool GetSoname(std::string* name) = 0;
+ virtual std::string GetSoname() = 0;
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
+ virtual std::string GetBuildID() = 0;
+
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
virtual bool IsValidPc(uint64_t pc);
@@ -85,6 +87,8 @@
uint64_t debug_frame_size() { return debug_frame_size_; }
uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; }
uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; }
+ uint64_t gnu_build_id_offset() { return gnu_build_id_offset_; }
+ uint64_t gnu_build_id_size() { return gnu_build_id_size_; }
DwarfSection* eh_frame() { return eh_frame_.get(); }
DwarfSection* debug_frame() { return debug_frame_.get(); }
@@ -96,6 +100,9 @@
template <typename EhdrType, typename PhdrType>
static uint64_t GetLoadBias(Memory* memory);
+ template <typename EhdrType, typename ShdrType, typename NhdrType>
+ static std::string ReadBuildIDFromMemory(Memory* memory);
+
protected:
template <typename AddressType>
void InitHeadersWithTemplate(uint64_t load_bias);
@@ -110,7 +117,7 @@
void ReadSectionHeaders(const EhdrType& ehdr);
template <typename DynType>
- bool GetSonameWithTemplate(std::string* soname);
+ std::string GetSonameWithTemplate();
template <typename SymType>
bool GetFunctionNameWithTemplate(uint64_t addr, std::string* name, uint64_t* func_offset);
@@ -123,6 +130,9 @@
template <typename EhdrType>
static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+ template <typename NhdrType>
+ std::string ReadBuildID();
+
Memory* memory_;
std::unordered_map<uint64_t, LoadInfo> pt_loads_;
@@ -143,6 +153,9 @@
uint64_t gnu_debugdata_offset_ = 0;
uint64_t gnu_debugdata_size_ = 0;
+ uint64_t gnu_build_id_offset_ = 0;
+ uint64_t gnu_build_id_size_ = 0;
+
uint8_t soname_type_ = SONAME_UNKNOWN;
std::string soname_;
@@ -170,9 +183,7 @@
ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
@@ -182,6 +193,8 @@
return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
}
+ std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf32_Nhdr>(); }
+
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
}
@@ -200,9 +213,7 @@
ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
}
- bool GetSoname(std::string* soname) override {
- return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(soname);
- }
+ std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
@@ -212,6 +223,8 @@
return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
}
+ std::string GetBuildID() override { return ElfInterface::ReadBuildID<Elf64_Nhdr>(); }
+
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
}
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 6ed0e0f..72ec454 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -29,6 +29,7 @@
ERROR_INVALID_MAP, // Unwind in an invalid map.
ERROR_MAX_FRAMES_EXCEEDED, // The number of frames exceed the total allowed.
ERROR_REPEATED_FRAME, // The last frame has the same pc/sp as the next.
+ ERROR_INVALID_ELF, // Unwind in an invalid elf.
};
struct ErrorData {
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index f64b04f..8b7b4b5 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -38,7 +38,7 @@
public:
explicit JitDebug(std::shared_ptr<Memory>& memory);
JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
- ~JitDebug();
+ virtual ~JitDebug();
Elf* GetElf(Maps* maps, uint64_t pc);
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 9c6b552..13ce10f 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -28,34 +28,30 @@
namespace unwindstack {
-// Forward declarations.
-class Maps;
-class Memory;
+class MemoryFileAtOffset;
struct MapInfo {
- MapInfo(Maps* maps) : maps_(maps) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end) : maps_(maps), start(start), end(end) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name)
- : maps_(maps),
- start(start),
+ : start(start),
end(end),
offset(offset),
flags(flags),
name(name),
- load_bias(static_cast<uint64_t>(-1)) {}
- MapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ prev_map(map_info),
+ load_bias(static_cast<uint64_t>(-1)),
+ build_id(0) {}
+ MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const std::string& name)
- : maps_(maps),
- start(start),
+ : start(start),
end(end),
offset(offset),
flags(flags),
name(name),
- load_bias(static_cast<uint64_t>(-1)) {}
- ~MapInfo() = default;
-
- Maps* maps_ = nullptr;
+ prev_map(map_info),
+ load_bias(static_cast<uint64_t>(-1)),
+ build_id(0) {}
+ ~MapInfo();
uint64_t start = 0;
uint64_t end = 0;
@@ -64,13 +60,25 @@
std::string name;
std::shared_ptr<Elf> elf;
// This value is only non-zero if the offset is non-zero but there is
- // no elf signature found at that offset. This indicates that the
- // entire file is represented by the Memory object returned by CreateMemory,
- // instead of a portion of the file.
+ // no elf signature found at that offset.
uint64_t elf_offset = 0;
+ // This value is the offset from the map in memory that is the start
+ // of the elf. This is not equal to offset when the linker splits
+ // shared libraries into a read-only and read-execute map.
+ uint64_t elf_start_offset = 0;
+
+ MapInfo* prev_map = nullptr;
std::atomic_uint64_t load_bias;
+ // This is a pointer to a new'd std::string.
+ // Using an atomic value means that we don't need to lock and will
+ // make it easier to move to a fine grained lock in the future.
+ std::atomic_uintptr_t build_id;
+
+ // Set to true if the elf file data is coming from memory.
+ bool memory_backed_elf = false;
+
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
@@ -78,11 +86,20 @@
Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
+ bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+
+ // Returns the raw build id read from the elf data.
+ std::string GetBuildID();
+
+ // Returns the printable version of the build id (hex dump of raw data).
+ std::string GetPrintableBuildID();
+
private:
MapInfo(const MapInfo&) = delete;
void operator=(const MapInfo&) = delete;
Memory* GetFileMemory();
+ bool InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory);
// Protect the creation of the elf object.
std::mutex mutex_;
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 67fbed2..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <vector>
@@ -37,8 +38,16 @@
class Maps {
public:
+ virtual ~Maps() = default;
+
Maps() = default;
- virtual ~Maps();
+
+ // Maps are not copyable but movable, because they own pointers to MapInfo
+ // objects.
+ Maps(const Maps&) = delete;
+ Maps& operator=(const Maps&) = delete;
+ Maps(Maps&&) = default;
+ Maps& operator=(Maps&&) = default;
MapInfo* Find(uint64_t pc);
@@ -51,11 +60,11 @@
void Sort();
- typedef std::vector<MapInfo*>::iterator iterator;
+ typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
- typedef std::vector<MapInfo*>::const_iterator const_iterator;
+ typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
const_iterator begin() const { return maps_.begin(); }
const_iterator end() const { return maps_.end(); }
@@ -63,11 +72,11 @@
MapInfo* Get(size_t index) {
if (index >= maps_.size()) return nullptr;
- return maps_[index];
+ return maps_[index].get();
}
protected:
- std::vector<MapInfo*> maps_;
+ std::vector<std::unique_ptr<MapInfo>> maps_;
};
class RemoteMaps : public Maps {
@@ -90,14 +99,14 @@
class LocalUpdatableMaps : public Maps {
public:
LocalUpdatableMaps() : Maps() {}
- virtual ~LocalUpdatableMaps();
+ virtual ~LocalUpdatableMaps() = default;
bool Reparse();
const std::string GetMapsFile() const override;
private:
- std::vector<MapInfo*> saved_maps_;
+ std::vector<std::unique_ptr<MapInfo>> saved_maps_;
};
class BufferMaps : public Maps {
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 9c425cb..3106564 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,11 +21,8 @@
#include <sys/types.h>
#include <unistd.h>
-#include <atomic>
-#include <map>
#include <memory>
#include <string>
-#include <vector>
namespace unwindstack {
@@ -35,9 +32,15 @@
virtual ~Memory() = default;
static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+ static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
+ static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start,
+ uint64_t end);
+ static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+ virtual void Clear() {}
+
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
bool ReadFully(uint64_t addr, void* dst, size_t size);
@@ -51,139 +54,6 @@
}
};
-class MemoryBuffer : public Memory {
- public:
- MemoryBuffer() = default;
- virtual ~MemoryBuffer() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- uint8_t* GetPtr(size_t offset);
-
- void Resize(size_t size) { raw_.resize(size); }
-
- uint64_t Size() { return raw_.size(); }
-
- private:
- std::vector<uint8_t> raw_;
-};
-
-class MemoryFileAtOffset : public Memory {
- public:
- MemoryFileAtOffset() = default;
- virtual ~MemoryFileAtOffset();
-
- bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- size_t Size() { return size_; }
-
- void Clear();
-
- protected:
- size_t size_ = 0;
- size_t offset_ = 0;
- uint8_t* data_ = nullptr;
-};
-
-class MemoryRemote : public Memory {
- public:
- MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
- virtual ~MemoryRemote() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- pid_t pid() { return pid_; }
-
- private:
- pid_t pid_;
- std::atomic_uintptr_t read_redirect_func_;
-};
-
-class MemoryLocal : public Memory {
- public:
- MemoryLocal() = default;
- virtual ~MemoryLocal() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-};
-
-// MemoryRange maps one address range onto another.
-// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
-// such that range.read(offset) is equivalent to underlying.read(src_begin).
-class MemoryRange : public Memory {
- public:
- MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
- uint64_t offset);
- virtual ~MemoryRange() = default;
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- uint64_t offset() { return offset_; }
- uint64_t length() { return length_; }
-
- private:
- std::shared_ptr<Memory> memory_;
- uint64_t begin_;
- uint64_t length_;
- uint64_t offset_;
-};
-
-class MemoryRanges : public Memory {
- public:
- MemoryRanges() = default;
- virtual ~MemoryRanges() = default;
-
- void Insert(MemoryRange* memory);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
-};
-
-class MemoryOffline : public Memory {
- public:
- MemoryOffline() = default;
- virtual ~MemoryOffline() = default;
-
- bool Init(const std::string& file, uint64_t offset);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::unique_ptr<MemoryRange> memory_;
-};
-
-class MemoryOfflineBuffer : public Memory {
- public:
- MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
- virtual ~MemoryOfflineBuffer() = default;
-
- void Reset(const uint8_t* data, uint64_t start, uint64_t end);
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- const uint8_t* data_;
- uint64_t start_;
- uint64_t end_;
-};
-
-class MemoryOfflineParts : public Memory {
- public:
- MemoryOfflineParts() = default;
- virtual ~MemoryOfflineParts();
-
- void Add(MemoryOffline* memory) { memories_.push_back(memory); }
-
- size_t Read(uint64_t addr, void* dst, size_t size) override;
-
- private:
- std::vector<MemoryOffline*> memories_;
-};
-
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 878ced3..1c2a81c 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -18,6 +18,7 @@
#define _LIBUNWINDSTACK_REGS_H
#include <stdint.h>
+#include <unistd.h>
#include <functional>
#include <string>
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 56b0581..52b3578 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,7 +24,9 @@
#include <string>
#include <vector>
+#include <unwindstack/DexFiles.h>
#include <unwindstack/Error.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -32,9 +34,7 @@
namespace unwindstack {
// Forward declarations.
-class DexFiles;
class Elf;
-class JitDebug;
enum ArchEnum : uint8_t;
struct FrameData {
@@ -48,7 +48,13 @@
uint64_t function_offset = 0;
std::string map_name;
- uint64_t map_offset = 0;
+ // The offset from the first map representing the frame. When there are
+ // two maps (read-only and read-execute) this will be the offset from
+ // the read-only map. When there is only one map, this will be the
+ // same as the actual offset of the map and match map_exact_offset.
+ uint64_t map_elf_start_offset = 0;
+ // The actual offset from the map where the pc lies.
+ uint64_t map_exact_offset = 0;
uint64_t map_start = 0;
uint64_t map_end = 0;
uint64_t map_load_bias = 0;
@@ -61,7 +67,12 @@
: max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
frames_.reserve(max_frames);
}
- ~Unwinder() = default;
+ Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
+ : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
+ frames_.reserve(max_frames);
+ }
+
+ virtual ~Unwinder() = default;
void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
@@ -70,26 +81,46 @@
const std::vector<FrameData>& frames() { return frames_; }
+ std::vector<FrameData> ConsumeFrames() {
+ std::vector<FrameData> frames = std::move(frames_);
+ frames_.clear();
+ return frames;
+ }
+
std::string FormatFrame(size_t frame_num);
- static std::string FormatFrame(const FrameData& frame, bool is32bit);
+ std::string FormatFrame(const FrameData& frame);
void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
+ void SetRegs(Regs* regs) { regs_ = regs; }
+ Maps* GetMaps() { return maps_; }
+ std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+
// Disabling the resolving of names results in the function name being
// set to an empty string and the function offset being set to zero.
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+ // Enable/disable soname printing the soname for a map name if the elf is
+ // embedded in a file. This is enabled by default.
+ // NOTE: This does nothing unless resolving names is enabled.
+ void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
+
+ void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
+
#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
#endif
+ bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
+
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
- private:
+ protected:
+ Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }
+
void FillInDexFrame();
- void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
- uint64_t pc_adjustment);
+ FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
size_t max_frames_;
Maps* maps_;
@@ -101,9 +132,30 @@
DexFiles* dex_files_ = nullptr;
#endif
bool resolve_names_ = true;
+ bool embedded_soname_ = true;
+ bool display_build_id_ = false;
+ // True if at least one elf file is coming from memory and not the related
+ // file. This is only true if there is an actual file backing up the elf.
+ bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
};
+class UnwinderFromPid : public Unwinder {
+ public:
+ UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
+ virtual ~UnwinderFromPid() = default;
+
+ bool Init(ArchEnum arch);
+
+ private:
+ pid_t pid_;
+ std::unique_ptr<Maps> maps_ptr_;
+ std::unique_ptr<JitDebug> jit_debug_ptr_;
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ std::unique_ptr<DexFiles> dex_files_ptr_;
+#endif
+};
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 5f3d1ea..7b1dd92 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -1662,7 +1662,7 @@
ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
}
-INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
- ::testing::Values("logging", "register_logging", "no_logging"));
+INSTANTIATE_TEST_SUITE_P(, ArmExidxDecodeTest,
+ ::testing::Values("logging", "register_logging", "no_logging"));
} // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 40f9f8e..0149a42 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -20,45 +20,35 @@
#include <unordered_map>
-#include <android-base/test_utils.h>
-
+#include <android-base/file.h>
+#include <gtest/gtest.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
-#include <dex/code_item_accessors-inl.h>
-#include <dex/standard_dex_file.h>
-
-#include <gtest/gtest.h>
-
#include "DexFile.h"
-
#include "DexFileData.h"
#include "MemoryFake.h"
namespace unwindstack {
TEST(DexFileTest, from_file_open_non_exist) {
- DexFileFromFile dex_file;
- ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
+ EXPECT_TRUE(DexFileFromFile::Create(0, "/file/does/not/exist") == nullptr);
}
TEST(DexFileTest, from_file_open_too_small) {
TemporaryFile tf;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_EQ(sizeof(art::DexFile::Header) - 2,
- static_cast<size_t>(
- TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
+ ASSERT_EQ(size_t{10}, static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, 10))));
// Header too small.
- DexFileFromFile dex_file;
- ASSERT_FALSE(dex_file.Open(0, tf.path));
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
// Header correct, file too small.
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
- ASSERT_EQ(sizeof(art::DexFile::Header), static_cast<size_t>(TEMP_FAILURE_RETRY(write(
- tf.fd, kDexData, sizeof(art::DexFile::Header)))));
- ASSERT_FALSE(dex_file.Open(0, tf.path));
+ ASSERT_EQ(sizeof(kDexData) - 1,
+ static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData) - 1))));
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) == nullptr);
}
TEST(DexFileTest, from_file_open) {
@@ -68,8 +58,7 @@
ASSERT_EQ(sizeof(kDexData),
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
- DexFileFromFile dex_file;
- ASSERT_TRUE(dex_file.Open(0, tf.path));
+ EXPECT_TRUE(DexFileFromFile::Create(0, tf.path) != nullptr);
}
TEST(DexFileTest, from_file_open_non_zero_offset) {
@@ -80,35 +69,31 @@
ASSERT_EQ(sizeof(kDexData),
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
- DexFileFromFile dex_file;
- ASSERT_TRUE(dex_file.Open(0x100, tf.path));
+ EXPECT_TRUE(DexFileFromFile::Create(0x100, tf.path) != nullptr);
}
TEST(DexFileTest, from_memory_fail_too_small_for_header) {
MemoryFake memory;
- memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
- DexFileFromMemory dex_file;
+ memory.SetMemory(0x1000, kDexData, 10);
- ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
}
TEST(DexFileTest, from_memory_fail_too_small_for_data) {
MemoryFake memory;
memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
- DexFileFromMemory dex_file;
- ASSERT_FALSE(dex_file.Open(0x1000, &memory));
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") == nullptr);
}
TEST(DexFileTest, from_memory_open) {
MemoryFake memory;
memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
- DexFileFromMemory dex_file;
- ASSERT_TRUE(dex_file.Open(0x1000, &memory));
+ EXPECT_TRUE(DexFileFromMemory::Create(0x1000, &memory, "") != nullptr);
}
TEST(DexFileTest, create_using_file) {
@@ -121,8 +106,7 @@
MemoryFake memory;
MapInfo info(nullptr, 0, 0x10000, 0, 0x5, tf.path);
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
- ASSERT_TRUE(dex_file != nullptr);
+ EXPECT_TRUE(DexFile::Create(0x500, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -135,8 +119,7 @@
MemoryFake memory;
MapInfo info(nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
- ASSERT_TRUE(dex_file != nullptr);
+ EXPECT_TRUE(DexFile::Create(0x600, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -149,24 +132,21 @@
MemoryFake memory;
MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
- ASSERT_TRUE(dex_file != nullptr);
+ EXPECT_TRUE(DexFile::Create(0x400, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "");
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
- ASSERT_TRUE(dex_file != nullptr);
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
MapInfo info(nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
- ASSERT_TRUE(dex_file != nullptr);
+ EXPECT_TRUE(DexFile::Create(0x4000, &memory, &info) != nullptr);
}
TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -179,22 +159,13 @@
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
MapInfo info(nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
- std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
+ std::unique_ptr<DexFile> dex_file = DexFile::Create(0x4000, &memory, &info);
ASSERT_TRUE(dex_file != nullptr);
// Check it came from memory by clearing memory and verifying it fails.
memory.Clear();
- dex_file.reset(DexFile::Create(0x4000, &memory, &info));
- ASSERT_TRUE(dex_file == nullptr);
-}
-
-TEST(DexFileTest, get_method_not_opened) {
- std::string method("something");
- uint64_t method_offset = 100;
- DexFile dex_file;
- dex_file.GetMethodInformation(0x100, &method, &method_offset);
- EXPECT_EQ("something", method);
- EXPECT_EQ(100U, method_offset);
+ dex_file = DexFile::Create(0x4000, &memory, &info);
+ EXPECT_TRUE(dex_file == nullptr);
}
TEST(DexFileTest, get_method) {
@@ -213,15 +184,6 @@
ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
EXPECT_EQ("Main.main", method);
EXPECT_EQ(0U, method_offset);
-
- // Make sure that any data that is cached is still retrievable.
- ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
- EXPECT_EQ("Main.<init>", method);
- EXPECT_EQ(4U, method_offset);
-
- ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
- EXPECT_EQ("Main.main", method);
- EXPECT_EQ(1U, method_offset);
}
TEST(DexFileTest, get_method_empty) {
@@ -236,12 +198,6 @@
EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
- // Make sure that once the whole dex file has been cached, no problems occur.
- EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
-
- // Choose a value that is in the cached map, but not in a valid method.
- EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index bb2e8f0..9dd0cdd 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -66,7 +66,7 @@
DwarfCie cie_;
DwarfFde fde_;
};
-TYPED_TEST_CASE_P(DwarfCfaLogTest);
+TYPED_TEST_SUITE_P(DwarfCfaLogTest);
// NOTE: All class variable references have to be prefaced with this->.
@@ -763,17 +763,17 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
- cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
- cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
- cfa_undefined, cfa_same, cfa_register, cfa_state,
- cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
- cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
- cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
- cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 7395b04..dd71490 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -64,7 +64,7 @@
DwarfCie cie_;
DwarfFde fde_;
};
-TYPED_TEST_CASE_P(DwarfCfaTest);
+TYPED_TEST_SUITE_P(DwarfCfaTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -952,16 +952,17 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
- cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
- cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
- cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
- cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
- cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
- cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
- cfa_gnu_negative_offset_extended, cfa_register_override);
+REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
+ cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
+ cfa_register_override);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaTest, DwarfCfaTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index d620934..2b36f17 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -44,7 +44,7 @@
MemoryFake memory_;
DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfDebugFrameTest);
+TYPED_TEST_SUITE_P(DwarfDebugFrameTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -550,6 +550,22 @@
VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
}
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
@@ -558,10 +574,10 @@
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
}
@@ -796,18 +812,19 @@
EXPECT_EQ(0xb50U, fde->pc_end);
}
-REGISTER_TYPED_TEST_CASE_P(
+REGISTER_TYPED_TEST_SUITE_P(
DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
- GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
- GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
- GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
+ GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+ GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
+ GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
+ GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 9cac6e8..4792fb5 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -42,7 +42,7 @@
MemoryFake memory_;
DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfEhFrameTest);
+TYPED_TEST_SUITE_P(DwarfEhFrameTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -125,9 +125,9 @@
EXPECT_EQ(1U, cie->return_address_register);
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 910ae36..78608e3 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -73,7 +73,7 @@
MemoryFake memory_;
TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
+TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -95,6 +95,13 @@
EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
+ // Verify a zero table entry size fails to init.
+ this->memory_.SetData8(0x1003, 0x1);
+ ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->eh_frame_->LastErrorCode());
+ // Reset the value back to the original.
+ this->memory_.SetData8(0x1003, DW_EH_PE_sdata4);
+
// Verify a zero fde count fails to init.
this->memory_.SetData32(0x1006, 0);
ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100, 0));
@@ -276,9 +283,8 @@
EXPECT_EQ(0x500U, info->offset);
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_verify) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_verify) {
this->eh_frame_->TestSetTableEntrySize(0x10);
- this->eh_frame_->TestSetFdeCount(10);
typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
for (size_t i = 0; i < 10; i++) {
@@ -288,105 +294,42 @@
}
uint64_t fde_offset;
- EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10));
+ this->eh_frame_->TestSetFdeCount(10);
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset));
// Not an error, just not found.
ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
// Even number of elements.
for (size_t i = 0; i < 10; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
TypeParam pc = 0x1000 * (i + 1);
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
}
+
// Odd number of elements.
+ this->eh_frame_->TestSetFdeCount(9);
for (size_t i = 0; i < 9; i++) {
+ SCOPED_TRACE(testing::Message() << "Failed at index " << i);
TypeParam pc = 0x1000 * (i + 1);
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
- EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9))
- << "Failed at index " << i;
- EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i;
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
+ EXPECT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset));
+ EXPECT_EQ(0x5000 + i * 0x20, fde_offset);
}
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetBinary_index_fail) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_index_fail) {
this->eh_frame_->TestSetTableEntrySize(0x10);
this->eh_frame_->TestSetFdeCount(10);
uint64_t fde_offset;
- EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x1000, &fde_offset, 10));
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential) {
- this->eh_frame_->TestSetFdeCount(10);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x2000);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- // Verify that if entries is zero, that it fails.
- uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
- this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x344, &fde_offset));
- EXPECT_EQ(0x500U, fde_offset);
-
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-
- // Expect that the data is cached so no more memory reads will occur.
- this->memory_.Clear();
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x444, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_last_element) {
- this->eh_frame_->TestSetFdeCount(2);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x2000);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
- this->eh_frame_->TestSetCurEntriesOffset(0x1040);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- uint64_t fde_offset;
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
- EXPECT_EQ(0x600U, fde_offset);
-}
-
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetSequential_end_check) {
- this->eh_frame_->TestSetFdeCount(2);
- this->eh_frame_->TestSetEntriesDataOffset(0x100);
- this->eh_frame_->TestSetEntriesEnd(0x1048);
- this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-
- this->memory_.SetData32(0x1040, 0x340);
- this->memory_.SetData32(0x1044, 0x500);
-
- this->memory_.SetData32(0x1048, 0x440);
- this->memory_.SetData32(0x104c, 0x600);
-
- uint64_t fde_offset;
- ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset));
- ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
+ EXPECT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset));
}
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_fail_fde_count) {
@@ -397,7 +340,7 @@
ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->LastErrorCode());
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_binary_search) {
+TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_search) {
this->eh_frame_->TestSetTableEntrySize(16);
this->eh_frame_->TestSetFdeCount(10);
@@ -417,26 +360,6 @@
EXPECT_EQ(0x10700U, fde_offset);
}
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeOffsetFromPc_sequential_search) {
- this->eh_frame_->TestSetFdeCount(10);
- this->eh_frame_->TestSetTableEntrySize(0);
-
- typename DwarfEhFrameWithHdr<TypeParam>::FdeInfo info;
- info.pc = 0x50;
- info.offset = 0x10000;
- this->eh_frame_->TestSetFdeInfo(0, info);
- info.pc = 0x150;
- info.offset = 0x10100;
- this->eh_frame_->TestSetFdeInfo(1, info);
- info.pc = 0x250;
- info.offset = 0x10200;
- this->eh_frame_->TestSetFdeInfo(2, info);
-
- uint64_t fde_offset;
- ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset));
- EXPECT_EQ(0x10100U, fde_offset);
-}
-
TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetCieFde32) {
// CIE 32 information.
this->memory_.SetData32(0xf000, 0x100);
@@ -523,16 +446,14 @@
ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
}
-REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
- GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
- GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
- GetFdeOffsetBinary_verify, GetFdeOffsetBinary_index_fail,
- GetFdeOffsetSequential, GetFdeOffsetSequential_last_element,
- GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count,
- GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search,
- GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
+REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+ GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+ GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+ GetFdeOffsetFromPc_verify, GetFdeOffsetFromPc_index_fail,
+ GetFdeOffsetFromPc_fail_fde_count, GetFdeOffsetFromPc_search,
+ GetCieFde32, GetCieFde64, GetFdeFromPc_fde_not_found);
typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameWithHdrTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index f12d2fe..650e965 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -54,6 +54,8 @@
void ReadEncodedValue_overflow();
template <typename AddressType>
void ReadEncodedValue_high_bit_set();
+ template <typename AddressType>
+ void ReadEncodedValue_all();
MemoryFake memory_;
std::unique_ptr<DwarfMemory> dwarf_mem_;
@@ -457,6 +459,27 @@
ReadEncodedValue_high_bit_set<uint64_t>();
}
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_all() {
+ MemoryFakeAlwaysReadZero memory;
+ DwarfMemory dwarf_mem(&memory);
+
+ for (size_t i = 0; i <= 0xff; i++) {
+ uint64_t value;
+ if (dwarf_mem.ReadEncodedValue<AddressType>(static_cast<uint8_t>(i), &value)) {
+ ASSERT_EQ(0U, value);
+ }
+ }
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint32_t) {
+ ReadEncodedValue_all<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_all_uint64_t) {
+ ReadEncodedValue_all<uint64_t>();
+}
+
TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
uint64_t value = 0x1234;
ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 3f09dd8..f4ade5d 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -48,7 +48,7 @@
std::unique_ptr<DwarfMemory> mem_;
std::unique_ptr<DwarfOp<TypeParam>> op_;
};
-TYPED_TEST_CASE_P(DwarfOpLogTest);
+TYPED_TEST_SUITE_P(DwarfOpLogTest);
TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
// Multi operation opcodes.
@@ -65,9 +65,9 @@
ASSERT_EQ(expected, lines);
}
-REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index d424d5f..0898ec0 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -48,7 +48,7 @@
std::unique_ptr<DwarfMemory> mem_;
std::unique_ptr<DwarfOp<TypeParam>> op_;
};
-TYPED_TEST_CASE_P(DwarfOpTest);
+TYPED_TEST_SUITE_P(DwarfOpTest);
TYPED_TEST_P(DwarfOpTest, decode) {
// Memory error.
@@ -1571,15 +1571,16 @@
EXPECT_FALSE(this->op_->dex_pc_set());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
- op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
- const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
- op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
- op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
- compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
- op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
+REGISTER_TYPED_TEST_SUITE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+ op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+ const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+ op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or,
+ op_plus, op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+ compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+ op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop,
+ is_dex_pc);
typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpTest, DwarfOpTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 46f555a..b386ef4 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -68,7 +68,7 @@
MemoryFake memory_;
TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
};
-TYPED_TEST_CASE_P(DwarfSectionImplTest);
+TYPED_TEST_SUITE_P(DwarfSectionImplTest);
// NOTE: All test class variables need to be referenced as this->.
@@ -571,18 +571,18 @@
ASSERT_EQ("", GetFakeLogBuf());
}
-REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
- GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
- Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
- Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
- Eval_cfa_register_prev, Eval_cfa_register_from_value,
- Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
- Eval_invalid_register, Eval_different_reg_locations,
- Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
- Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
- GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
+REGISTER_TYPED_TEST_SUITE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+ GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+ Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+ Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+ Eval_cfa_register_prev, Eval_cfa_register_from_value,
+ Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+ Eval_invalid_register, Eval_different_reg_locations,
+ Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+ Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index d9acdec..5735858 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -18,7 +18,6 @@
#include <unistd.h>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
@@ -32,7 +31,7 @@
class ElfCacheTest : public ::testing::Test {
protected:
- static void SetUpTestCase() { memory_.reset(new MemoryFake); }
+ static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
void SetUp() override { Elf::SetCachingEnabled(true); }
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index a3bf5ce..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -68,10 +68,11 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return fake_soname_; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
bool GetGlobalVariable(const std::string&, uint64_t*) override;
+ std::string GetBuildID() override { return fake_build_id_; }
bool Step(uint64_t, Regs*, Memory*, bool*) override;
@@ -79,6 +80,11 @@
globals_[global] = offset;
}
+ void FakeSetBuildID(std::string& build_id) { fake_build_id_ = build_id; }
+ void FakeSetBuildID(const char* build_id) { fake_build_id_ = build_id; }
+
+ void FakeSetSoname(const char* soname) { fake_soname_ = soname; }
+
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -93,6 +99,8 @@
private:
std::unordered_map<std::string, uint64_t> globals_;
+ std::string fake_build_id_;
+ std::string fake_soname_;
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index 9326bff..f9ee9eb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -116,6 +116,21 @@
void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
uint64_t sym_offset, const char* name);
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildID();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDTwoNotes();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForName();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForDesc();
+
+ template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+ void BuildIDSectionTooSmallForHeader();
+
MemoryFake memory_;
};
@@ -345,7 +360,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0x1001U, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(1U, pt_loads.size());
@@ -540,9 +555,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_TRUE(elf->GetSoname(&name));
- ASSERT_STREQ("fake_soname.so", name.c_str());
+ ASSERT_EQ("fake_soname.so", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname) {
@@ -563,8 +576,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
@@ -585,8 +597,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_size) {
@@ -609,8 +620,7 @@
ASSERT_TRUE(elf->Init(&load_bias));
EXPECT_EQ(0U, load_bias);
- std::string name;
- ASSERT_FALSE(elf->GetSoname(&name));
+ ASSERT_EQ("", elf->GetSoname());
}
TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
@@ -898,7 +908,7 @@
Ehdr ehdr = {};
ehdr.e_shoff = offset;
- ehdr.e_shnum = 6;
+ ehdr.e_shnum = 7;
ehdr.e_shentsize = sizeof(Shdr);
ehdr.e_shstrndx = 2;
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
@@ -958,10 +968,19 @@
memory_.SetMemory(offset, &shdr, sizeof(shdr));
offset += ehdr.e_shentsize;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = 0xf00;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
memory_.SetMemory(0xf200, ".gnu_debugdata", sizeof(".gnu_debugdata"));
memory_.SetMemory(0xf300, ".eh_frame", sizeof(".eh_frame"));
memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
@@ -974,6 +993,8 @@
EXPECT_EQ(0x800U, elf->eh_frame_size());
EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
EXPECT_EQ(0xf00U, elf->eh_frame_hdr_size());
+ EXPECT_EQ(0xb000U, elf->gnu_build_id_offset());
+ EXPECT_EQ(0xf00U, elf->gnu_build_id_size());
}
TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
@@ -1153,4 +1174,325 @@
EXPECT_FALSE(elf->IsValidPc(0x2a00));
}
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildID() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDTwoNotes() {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 8; // "WRONG" aligned to 4
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ memcpy(¬e_section[note_offset], "WRONG", sizeof("WRONG"));
+ note_offset += 8;
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section[note_offset], ¬e_header, sizeof(note_header));
+ note_offset += sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("BUILDID", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) + 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) + sizeof("GNU") + 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
+void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+
+ uint64_t offset = 0x2000;
+
+ Ehdr ehdr = {};
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ offset += ehdr.e_shentsize;
+
+ char note_section[128];
+ Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 7; // "BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
+
+ Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_header) - 1;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ memory_.SetMemory(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
+ memory_.SetMemory(0xb000, note_section, sizeof(note_section));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ ASSERT_EQ("", elf->GetBuildID());
+}
+
+TEST_F(ElfInterfaceTest, build_id32) {
+ BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id64) {
+ BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes32) {
+ BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_two_notes64) {
+ BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) {
+ BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) {
+ BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) {
+ BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) {
+ BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) {
+ BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
+}
+
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
+ BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index ccf8927..c432d6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -126,14 +126,18 @@
ASSERT_FALSE(elf.valid());
ASSERT_TRUE(elf.interface() == nullptr);
- std::string name;
- ASSERT_FALSE(elf.GetSoname(&name));
+ ASSERT_EQ("", elf.GetSoname());
+ std::string name;
uint64_t func_offset;
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
+ ASSERT_FALSE(elf.StepIfSignalHandler(0, nullptr, nullptr));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
+
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
+ EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -269,7 +273,7 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(nullptr, 0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
@@ -295,9 +299,8 @@
}
elf.FakeSetValid(true);
- bool finished;
- ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
- EXPECT_FALSE(finished);
+ ASSERT_TRUE(elf.StepIfSignalHandler(0x3000, ®s, &process_memory));
+ EXPECT_EQ(ERROR_NONE, elf.GetLastErrorCode());
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
}
@@ -309,8 +312,9 @@
bool Init(uint64_t*) override { return false; }
void InitHeaders(uint64_t) override {}
- bool GetSoname(std::string*) override { return false; }
+ std::string GetSoname() override { return ""; }
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
+ std::string GetBuildID() override { return ""; }
MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
@@ -335,7 +339,7 @@
EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libutils/tests/Mutex_test.cpp b/libunwindstack/tests/IsolatedSettings.cpp
similarity index 66%
copy from libutils/tests/Mutex_test.cpp
copy to libunwindstack/tests/IsolatedSettings.cpp
index 8a1805f..dbd8bd6 100644
--- a/libutils/tests/Mutex_test.cpp
+++ b/libunwindstack/tests/IsolatedSettings.cpp
@@ -14,19 +14,13 @@
* limitations under the License.
*/
-#include <utils/Mutex.h>
+#include <stdint.h>
+#include <stdio.h>
-#include <gtest/gtest.h>
-
-static android::Mutex mLock;
-static int i GUARDED_BY(mLock);
-
-void modifyLockedVariable() REQUIRES(mLock) {
- i = 1;
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+ static const char* initial_args[2] = {"--slow_threshold_ms=90000",
+ "--deadline_threshold_ms=120000"};
+ *args = initial_args;
+ *num_args = 2;
+ return true;
}
-
-TEST(Mutex, compile) {
- android::Mutex::Autolock _l(mLock);
- i = 0;
- modifyLockedVariable();
-}
\ No newline at end of file
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 2a73c7e..6c1cfa2 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -27,7 +27,6 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
@@ -59,23 +58,21 @@
ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
}
- static void SetUpTestCase() {
- std::vector<uint8_t> buffer(1024);
- memset(buffer.data(), 0, buffer.size());
+ void SetUp() override {
+ std::vector<uint8_t> buffer(12288, 0);
memcpy(buffer.data(), ELFMAG, SELFMAG);
buffer[EI_CLASS] = ELFCLASS32;
- ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), 1024));
memset(buffer.data(), 0, buffer.size());
- memcpy(&buffer[0x100], ELFMAG, SELFMAG);
- buffer[0x100 + EI_CLASS] = ELFCLASS64;
- ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+ memcpy(&buffer[0x1000], ELFMAG, SELFMAG);
+ buffer[0x1000 + EI_CLASS] = ELFCLASS64;
+ buffer[0x2000] = 0xff;
+ ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, buffer.data(), buffer.size()));
InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
- }
- void SetUp() override {
memory_ = new MemoryFake;
process_memory_.reset(memory_);
}
@@ -83,17 +80,13 @@
MemoryFake* memory_;
std::shared_ptr<Memory> process_memory_;
- static TemporaryFile elf_;
+ TemporaryFile elf_;
- static TemporaryFile elf_at_100_;
+ TemporaryFile elf_at_1000_;
- static TemporaryFile elf32_at_map_;
- static TemporaryFile elf64_at_map_;
+ TemporaryFile elf32_at_map_;
+ TemporaryFile elf64_at_map_;
};
-TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
-TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
-TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
@@ -109,6 +102,7 @@
info.end = 0x101;
memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
}
// Verify that if the offset is non-zero but there is no elf at the offset,
@@ -118,7 +112,9 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
// Read the entire file.
std::vector<uint8_t> buffer(1024);
@@ -130,16 +126,64 @@
}
ASSERT_FALSE(memory->ReadFully(1024, buffer.data(), 1));
+
+ // Now verify the elf start offset is set correctly based on the previous
+ // info.
+ MapInfo prev_info(nullptr, 0, 0x100, 0x10, 0, "");
+ info.prev_map = &prev_info;
+
+ // No preconditions met, change each one until it should set the elf start
+ // offset to zero.
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.offset = 0;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.flags = PROT_READ;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0x100U, info.elf_start_offset);
+
+ prev_info.name = info.name;
+ info.elf_offset = 0;
+ info.elf_start_offset = 0;
+ info.memory_backed_elf = false;
+ memory.reset(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
+ ASSERT_EQ(0x100U, info.elf_offset);
+ EXPECT_EQ(0U, info.elf_start_offset);
}
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(nullptr, 0x100, 0x200, 0x100, 0, elf_at_100_.path);
+ MapInfo info(nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@@ -162,7 +206,9 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x1000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -178,7 +224,9 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(info.memory_backed_elf);
ASSERT_EQ(0U, info.elf_offset);
+ EXPECT_EQ(0x2000U, info.elf_start_offset);
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -218,6 +266,7 @@
std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_TRUE(info.memory_backed_elf);
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
@@ -249,8 +298,10 @@
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_TRUE(map_info->memory_backed_elf);
EXPECT_EQ(0x4000UL, map_info->elf_offset);
EXPECT_EQ(0x4000UL, map_info->offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@@ -294,8 +345,10 @@
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_TRUE(map_info->memory_backed_elf);
EXPECT_EQ(0x1000UL, map_info->elf_offset);
EXPECT_EQ(0xb000UL, map_info->offset);
+ EXPECT_EQ(0xa000UL, map_info->elf_start_offset);
// Verify that reading values from this memory works properly.
std::vector<uint8_t> buffer(0x4000);
@@ -313,4 +366,48 @@
}
}
+TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
+ Maps maps;
+ maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
+ maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
+ maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
+
+ MapInfo* map_info = maps.Find(0x2000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ // Set up the size
+ Elf64_Ehdr ehdr;
+ ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+ // Will not give the elf memory, because the read-only entry does not
+ // extend over the executable segment.
+ std::unique_ptr<Memory> memory(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ EXPECT_FALSE(map_info->memory_backed_elf);
+ std::vector<uint8_t> buffer(0x100);
+ EXPECT_EQ(0x2000U, map_info->offset);
+ EXPECT_EQ(0U, map_info->elf_offset);
+ EXPECT_EQ(0U, map_info->elf_start_offset);
+ ASSERT_TRUE(memory->ReadFully(0, buffer.data(), 0x100));
+ EXPECT_EQ(0xffU, buffer[0]);
+
+ // Now init the elf data enough so that the file memory object will be used.
+ ehdr.e_shoff = 0x4000;
+ ehdr.e_shnum = 1;
+ ehdr.e_shentsize = 0x100;
+ ASSERT_EQ(0x1000, lseek(elf_at_1000_.fd, 0x1000, SEEK_SET));
+ ASSERT_TRUE(android::base::WriteFully(elf_at_1000_.fd, &ehdr, sizeof(ehdr)));
+
+ map_info->memory_backed_elf = false;
+ memory.reset(map_info->CreateMemory(process_memory_));
+ EXPECT_FALSE(map_info->memory_backed_elf);
+ EXPECT_EQ(0x2000U, map_info->offset);
+ EXPECT_EQ(0x1000U, map_info->elf_offset);
+ EXPECT_EQ(0x1000U, map_info->elf_start_offset);
+ Elf64_Ehdr ehdr_mem;
+ ASSERT_TRUE(memory->ReadFully(0, &ehdr_mem, sizeof(ehdr_mem)));
+ EXPECT_TRUE(memcmp(&ehdr, &ehdr_mem, sizeof(ehdr)) == 0);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
new file mode 100644
index 0000000..16451d1
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "ElfTestUtils.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MapInfoGetBuildIDTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ tf_.reset(new TemporaryFile);
+
+ memory_ = new MemoryFake;
+ elf_ = new ElfFake(new MemoryFake);
+ elf_interface_ = new ElfInterfaceFake(memory_);
+ elf_->FakeSetInterface(elf_interface_);
+ elf_container_.reset(elf_);
+ map_info_.reset(new MapInfo(nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+ }
+
+ void MultipleThreadTest(std::string expected_build_id);
+
+ MemoryFake* memory_;
+ ElfFake* elf_;
+ ElfInterfaceFake* elf_interface_;
+ std::unique_ptr<ElfFake> elf_container_;
+ std::unique_ptr<MapInfo> map_info_;
+ std::unique_ptr<TemporaryFile> tf_;
+};
+
+TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
+ MapInfo info(nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+
+ EXPECT_EQ("", info.GetBuildID());
+ EXPECT_EQ("", info.GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf) {
+ map_info_->elf.reset(elf_container_.release());
+ elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+ EXPECT_EQ("FAKE_BUILD_ID", map_info_->GetBuildID());
+ EXPECT_EQ("46414b455f4255494c445f4944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_elf_no_sign_extension) {
+ map_info_->elf.reset(elf_container_.release());
+
+ std::string build_id = {static_cast<char>(0xfa), static_cast<char>(0xab), static_cast<char>(0x12),
+ static_cast<char>(0x02)};
+ elf_interface_->FakeSetBuildID(build_id);
+
+ EXPECT_EQ("\xFA\xAB\x12\x2", map_info_->GetBuildID());
+ EXPECT_EQ("faab1202", map_info_->GetPrintableBuildID());
+}
+
+void MapInfoGetBuildIDTest::MultipleThreadTest(std::string expected_build_id) {
+ static constexpr size_t kNumConcurrentThreads = 100;
+
+ std::string build_id_values[kNumConcurrentThreads];
+ std::vector<std::thread*> threads;
+
+ std::atomic_bool wait;
+ wait = true;
+ // Create all of the threads and have them do the GetLoadBias at the same time
+ // to make it likely that a race will occur.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ std::thread* thread = new std::thread([i, this, &wait, &build_id_values]() {
+ while (wait)
+ ;
+ build_id_values[i] = map_info_->GetBuildID();
+ });
+ threads.push_back(thread);
+ }
+
+ // Set them all going and wait for the threads to finish.
+ wait = false;
+ for (auto thread : threads) {
+ thread->join();
+ delete thread;
+ }
+
+ // Now verify that all of the elf files are exactly the same and valid.
+ for (size_t i = 0; i < kNumConcurrentThreads; i++) {
+ EXPECT_EQ(expected_build_id, build_id_values[i]) << "Thread " << i << " mismatched.";
+ }
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists) {
+ map_info_->elf.reset(elf_container_.release());
+ elf_interface_->FakeSetBuildID("FAKE_BUILD_ID");
+
+ MultipleThreadTest("FAKE_BUILD_ID");
+}
+
+static void InitElfData(int fd) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shstrndx = 2;
+ off_t offset = 0;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(ehdr)), write(fd, &ehdr, sizeof(ehdr)));
+
+ char note_section[128];
+ Elf32_Nhdr note_header = {};
+ note_header.n_namesz = 4; // "GNU"
+ note_header.n_descsz = 12; // "ELF_BUILDID"
+ note_header.n_type = NT_GNU_BUILD_ID;
+ memcpy(¬e_section, ¬e_header, sizeof(note_header));
+ size_t note_offset = sizeof(note_header);
+ memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
+ note_offset += sizeof("GNU");
+ memcpy(¬e_section[note_offset], "ELF_BUILDID", sizeof("ELF_BUILDID"));
+ note_offset += sizeof("ELF_BUILDID");
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NOTE;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0xb000;
+ shdr.sh_size = sizeof(note_section);
+ offset += ehdr.e_shoff + sizeof(shdr);
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+ // The string data for section header names.
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x20000;
+ shdr.sh_offset = 0xf000;
+ shdr.sh_size = 0x1000;
+ offset += sizeof(shdr);
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(shdr)), write(fd, &shdr, sizeof(shdr)));
+
+ offset = 0xf500;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(".note.gnu.build-id")),
+ write(fd, ".note.gnu.build-id", sizeof(".note.gnu.build-id")));
+
+ offset = 0xb000;
+ ASSERT_EQ(offset, lseek(fd, offset, SEEK_SET));
+ ASSERT_EQ(static_cast<ssize_t>(sizeof(note_section)),
+ write(fd, note_section, sizeof(note_section)));
+}
+
+TEST_F(MapInfoGetBuildIDTest, from_memory) {
+ InitElfData(tf_->fd);
+
+ EXPECT_EQ("ELF_BUILDID", map_info_->GetBuildID());
+ EXPECT_EQ("454c465f4255494c444944", map_info_->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, multiple_thread_elf_exists_in_memory) {
+ InitElfData(tf_->fd);
+
+ MultipleThreadTest("ELF_BUILDID");
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 4d74696..d60b8b1 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -29,7 +29,6 @@
#include <vector>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
@@ -89,6 +88,12 @@
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
EXPECT_EQ(ELFCLASS32, elf->class_type());
+
+ // Now verify that an empty process memory returns an invalid elf object.
+ info.elf.reset();
+ elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid64) {
@@ -291,27 +296,6 @@
ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
}
-TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
- MapInfo info(nullptr, 0x9000, 0xa000, 0x1000, 0, "");
-
- // Create valid elf data in process memory only.
- Elf64_Ehdr ehdr;
- TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
- ehdr.e_shoff = 0x2000;
- ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
- ehdr.e_shnum = 0;
- memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
-
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
- ASSERT_TRUE(elf != nullptr);
- ASSERT_FALSE(elf->valid());
-
- info.elf.reset();
- info.flags = PROT_READ;
- elf = info.GetElf(process_memory_, ARCH_ARM64);
- ASSERT_TRUE(elf->valid());
-}
-
TEST_F(MapInfoGetElfTest, check_device_maps) {
MapInfo info(nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
"/dev/something");
@@ -387,4 +371,35 @@
}
}
+// Verify that previous maps don't automatically get the same elf object.
+TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
+ MapInfo info1(nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ MapInfo info2(&info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
+ Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+}
+
+// Verify that a read-only map followed by a read-execute map will result
+// in the same elf object in both maps.
+TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf) {
+ MapInfo r_info(nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ MapInfo rw_info(&r_info, 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
+ Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+
+ ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..e2cbb98
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+namespace unwindstack {
+
+TEST(MapInfoTest, maps_constructor_const_char) {
+ MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, 1, 2, 3, 4, "map");
+
+ EXPECT_EQ(&prev_map, map_info.prev_map);
+ EXPECT_EQ(1UL, map_info.start);
+ EXPECT_EQ(2UL, map_info.end);
+ EXPECT_EQ(3UL, map_info.offset);
+ EXPECT_EQ(4UL, map_info.flags);
+ EXPECT_EQ("map", map_info.name);
+ EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+ EXPECT_EQ(0UL, map_info.elf_offset);
+ EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, maps_constructor_string) {
+ std::string name("string_map");
+ MapInfo prev_map(nullptr, 0, 0, 0, 0, "");
+ MapInfo map_info(&prev_map, 1, 2, 3, 4, name);
+
+ EXPECT_EQ(&prev_map, map_info.prev_map);
+ EXPECT_EQ(1UL, map_info.start);
+ EXPECT_EQ(2UL, map_info.end);
+ EXPECT_EQ(3UL, map_info.offset);
+ EXPECT_EQ(4UL, map_info.flags);
+ EXPECT_EQ("string_map", map_info.name);
+ EXPECT_EQ(static_cast<uint64_t>(-1), map_info.load_bias);
+ EXPECT_EQ(0UL, map_info.elf_offset);
+ EXPECT_TRUE(map_info.elf.get() == nullptr);
+}
+
+TEST(MapInfoTest, get_function_name) {
+ ElfFake* elf = new ElfFake(nullptr);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ elf->FakeSetInterface(interface);
+ interface->FakePushFunctionData(FunctionData("function", 1000));
+
+ MapInfo map_info(nullptr, 1, 2, 3, 4, "");
+ map_info.elf.reset(elf);
+
+ std::string name;
+ uint64_t offset;
+ ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+ EXPECT_EQ("function", name);
+ EXPECT_EQ(1000UL, offset);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 6bdd0b2..9e7a6ab 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -19,7 +19,6 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <unwindstack/Maps.h>
@@ -62,8 +61,28 @@
ASSERT_EQ(0U, info->load_bias.load());
}
+TEST(MapsTest, map_move) {
+ Maps maps;
+
+ maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
+ maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+
+ Maps maps2 = std::move(maps);
+
+ ASSERT_EQ(3U, maps2.Total());
+ MapInfo* info = maps2.Get(0);
+ ASSERT_EQ(0x1000U, info->start);
+ ASSERT_EQ(0x2000U, info->end);
+ ASSERT_EQ(0U, info->offset);
+ ASSERT_EQ(PROT_READ, info->flags);
+ ASSERT_EQ("fake_map", info->name);
+ ASSERT_EQ(0U, info->elf_offset);
+ ASSERT_EQ(0U, info->load_bias.load());
+}
+
TEST(MapsTest, verify_parse_line) {
- MapInfo info(nullptr);
+ MapInfo info(nullptr, 0, 0, 0, 0, "");
VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
EXPECT_EQ(1U, info.start);
@@ -136,7 +155,7 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info(nullptr);
+ MapInfo info(nullptr, 0, 0, 0, 0, "");
#if defined(__LP64__)
VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
EXPECT_EQ(0xfabcdef012345678UL, info.start);
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index 28e0e76..a6c12aa 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,9 +18,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "LogFake.h"
+#include "MemoryBuffer.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 0000000..3bd3e4d
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "MemoryCache.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ memory_cache_.reset(new MemoryCache(memory_));
+
+ memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+ memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+ }
+
+ MemoryFake* memory_;
+ std::unique_ptr<MemoryCache> memory_cache_;
+
+ constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used after a reset.
+ memory_cache_->Clear();
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+ std::vector<uint8_t> expect(16, 0xab);
+ expect.resize(32, 0xde);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+ std::vector<uint8_t> buffer(kMaxCachedSize);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+ std::vector<uint8_t> expect(16, 0xde);
+ expect.resize(32, 0x50);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is not used for the second half but for the first.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ expect.resize(16);
+ expect.resize(32, 0xff);
+ ASSERT_EQ(expect, buffer);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index d7d1ace..4124a49 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryFileAtOffset.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 5a389d0..c9e5dc0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -22,7 +22,7 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryLocal.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
index f022884..9531708 100644
--- a/libunwindstack/tests/MemoryOfflineBufferTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -18,9 +18,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "LogFake.h"
+#include "MemoryOfflineBuffer.h"
namespace unwindstack {
@@ -31,7 +30,7 @@
memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
}
- static void SetUpTestCase() {
+ static void SetUpTestSuite() {
buffer_.resize(kLength);
for (size_t i = 0; i < kLength; i++) {
buffer_[i] = i % 189;
diff --git a/libunwindstack/tests/MemoryOfflineTest.cpp b/libunwindstack/tests/MemoryOfflineTest.cpp
index 14d58e6..d0c441b 100644
--- a/libunwindstack/tests/MemoryOfflineTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -19,8 +19,8 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
-#include <android-base/test_utils.h>
-#include <unwindstack/Memory.h>
+
+#include "MemoryOffline.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 2bac95b..2d4f141 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -21,9 +21,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "MemoryFake.h"
+#include "MemoryRange.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
index d24fcd2..e4e9fc4 100644
--- a/libunwindstack/tests/MemoryRangesTest.cpp
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -20,9 +20,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
-
#include "MemoryFake.h"
+#include "MemoryRange.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index fb56e8a..c90dedc 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -30,7 +30,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
-#include <unwindstack/Memory.h>
+#include "MemoryRemote.h"
#include "MemoryFake.h"
#include "TestUtils.h"
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index d6ca9b7..207d46e 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -23,6 +23,8 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include "Check.h"
+
namespace unwindstack {
class RegsFake : public Regs {
@@ -47,7 +49,10 @@
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
- bool Is32Bit() { return false; }
+ bool Is32Bit() {
+ CHECK(fake_arch_ != ARCH_UNKNOWN);
+ return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS;
+ }
uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 9a27dbd..7e36953 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -236,7 +236,7 @@
}
using RegTypes = ::testing::Types<RegsArm, RegsArm64, RegsX86, RegsX86_64, RegsMips, RegsMips64>;
-TYPED_TEST_CASE(RegsIterateTest, RegTypes);
+TYPED_TEST_SUITE(RegsIterateTest, RegTypes);
TYPED_TEST(RegsIterateTest, iterate) {
std::vector<Register> expected = ExpectedRegisters<TypeParam>();
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 00264c2..472d1cf 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -182,7 +182,7 @@
RegsX86_64 regs_x86_64;
RegsMips regs_mips;
RegsMips64 regs_mips64;
- MapInfo map_info(nullptr, 0x1000, 0x2000);
+ MapInfo map_info(nullptr, 0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index b40a253..ae3c349 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -55,7 +55,7 @@
MemoryFake memory_;
};
-TYPED_TEST_CASE_P(SymbolsTest);
+TYPED_TEST_SUITE_P(SymbolsTest);
TYPED_TEST_P(SymbolsTest, function_bounds_check) {
Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
@@ -362,11 +362,11 @@
EXPECT_EQ(4U, offset);
}
-REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
- multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
- symtab_read_cached, get_global);
+REGISTER_TYPED_TEST_SUITE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
+ multiple_entries_nonstandard_size, symtab_value_out_of_bounds,
+ symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
-INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
+INSTANTIATE_TYPED_TEST_SUITE_P(, SymbolsTest, SymbolsTestTypes);
} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.cpp b/libunwindstack/tests/TestUtils.cpp
new file mode 100644
index 0000000..e76f5f8
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <malloc.h>
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+namespace unwindstack {
+
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
+ static constexpr size_t kNumLeakLoops = 200;
+ static constexpr size_t kMaxAllowedLeakBytes = 32 * 1024;
+
+ size_t first_allocated_bytes = 0;
+ size_t last_allocated_bytes = 0;
+ for (size_t i = 0; i < kNumLeakLoops; i++) {
+ unwind_func(data);
+
+ size_t allocated_bytes = mallinfo().uordblks;
+ if (first_allocated_bytes == 0) {
+ first_allocated_bytes = allocated_bytes;
+ } else if (last_allocated_bytes > first_allocated_bytes) {
+ // Check that the memory did not increase too much over the first loop.
+ ASSERT_LE(last_allocated_bytes - first_allocated_bytes, kMaxAllowedLeakBytes);
+ }
+ last_allocated_bytes = allocated_bytes;
+ }
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 8c31aa6..a4d7b9b 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -50,6 +50,8 @@
return ready;
}
+void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index a65c077..bded57a 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -35,7 +35,6 @@
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/RegsX86.h>
@@ -43,6 +42,8 @@
#include <unwindstack/Unwinder.h>
#include "ElfTestUtils.h"
+#include "MemoryOffline.h"
+#include "TestUtils.h"
namespace unwindstack {
@@ -61,7 +62,7 @@
free(cwd_);
}
- void Init(const char* file_dir, ArchEnum arch) {
+ void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
dir_ = TestGetFileDirectory() + "offline/" + file_dir;
std::string data;
@@ -70,23 +71,25 @@
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
- std::string stack_name(dir_ + "stack.data");
- struct stat st;
- if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
- ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
- process_memory_.reset(stack_memory.release());
- } else {
- std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
- for (size_t i = 0;; i++) {
- stack_name = dir_ + "stack" + std::to_string(i) + ".data";
- if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
- ASSERT_TRUE(i != 0) << "No stack data files found.";
- break;
+ if (add_stack) {
+ std::string stack_name(dir_ + "stack.data");
+ struct stat st;
+ if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+ std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
+ ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
+ process_memory_.reset(stack_memory.release());
+ } else {
+ std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
+ for (size_t i = 0;; i++) {
+ stack_name = dir_ + "stack" + std::to_string(i) + ".data";
+ if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
+ ASSERT_TRUE(i != 0) << "No stack data files found.";
+ break;
+ }
+ AddMemory(stack_name, stack_memory.get());
}
- AddMemory(stack_name, stack_memory.get());
+ process_memory_.reset(stack_memory.release());
}
- process_memory_.reset(stack_memory.release());
}
switch (arch) {
@@ -203,6 +206,7 @@
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+ std::unique_ptr<Regs> regs_copy(regs_->Clone());
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -210,8 +214,8 @@
ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0001a9f8 libc.so (abort+64)\n"
- " #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
- " #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
" #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
frame_info);
EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
@@ -222,6 +226,22 @@
EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+ // Display build ids now.
+ unwinder.SetRegs(regs_copy.get());
+ unwinder.SetDisplayBuildID(true);
+ unwinder.Unwind();
+
+ frame_info = DumpFrames(unwinder);
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
+ frame_info);
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -234,9 +254,10 @@
ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0006dc49 libandroid_runtime.so "
- "(_ZN7android14AndroidRuntime15javaThreadShellEPv+80)\n"
+ "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
" #01 pc 0006dce5 libandroid_runtime.so "
- "(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
+ "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
+ "unsigned int, void**))\n",
frame_info);
EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
@@ -257,10 +278,10 @@
" #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
" #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
" #03 pc 000000000042d8fc libunwindstack_test "
- "(_ZN11unwindstackL19RemoteThroughSignalEij+20)\n"
+ "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
" #04 pc 000000000042d8d8 libunwindstack_test "
- "(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
- " #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
+ "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
+ " #05 pc 0000000000455d70 libunwindstack_test (testing::Test::Run()+392)\n",
frame_info);
EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
@@ -296,54 +317,57 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+ " #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
- " #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ " #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
" #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #05 pc 00146ab5 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
" #06 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #07 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #08 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #11 pc 0000fe03 anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
" #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #13 pc 00146ab5 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
" #14 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #15 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #16 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #19 pc 0000fd3b anonymous:ee74c000 (int Main.compare(java.lang.Object, "
"java.lang.Object)+107)\n"
" #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #21 pc 00146ab5 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
" #22 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #23 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #24 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #27 pc 0000fbdb anonymous:ee74c000 (int "
@@ -351,81 +375,86 @@
"java.util.Comparator)+331)\n"
" #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
" #29 pc 00146acb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
" #30 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #31 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #32 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #35 pc 0000f624 anonymous:ee74c000 (boolean Main.foo()+164)\n"
" #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #37 pc 00146ab5 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
" #38 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #39 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #40 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #43 pc 0000eedb anonymous:ee74c000 (void Main.runPrimary()+59)\n"
" #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #45 pc 00146ab5 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+885)\n"
" #46 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #47 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #48 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #51 pc 0000ac21 anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
" #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
" #53 pc 00146acb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
" #54 pc 0039cf0d libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+653)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
" #55 pc 00392552 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+354)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+354)\n"
" #56 pc 0039399a libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+234)\n"
" #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
" #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
" #60 pc 00146acb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+907)\n"
" #61 pc 005aac95 libartd.so "
- "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
- "8ArgArrayEPNS_6JValueEPKc+85)\n"
+ "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+ "art::ArgArray*, art::JValue*, char const*)+85)\n"
" #62 pc 005aab5a libartd.so "
- "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
- "jmethodIDPc+362)\n"
+ "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+ "_jmethodID*, char*)+362)\n"
" #63 pc 0048a3dd libartd.so "
- "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+ "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
" #64 pc 0018448c libartd.so "
- "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
- "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+ "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
+ "art::Primitive::Type, art::InvokeType)+1964)\n"
" #65 pc 0017cf06 libartd.so "
- "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+ "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
" #66 pc 00001d8c dalvikvm32 "
- "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+ "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
" #67 pc 00001a80 dalvikvm32 (main+1312)\n"
" #68 pc 00018275 libc.so\n",
frame_info);
@@ -591,38 +620,40 @@
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
- " #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ " #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
" #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #04 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
" #05 pc 000bf7a9 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
" #06 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #07 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #08 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #09 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #10 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #11 pc 00011c31 anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
" #12 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #13 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
" #14 pc 000bf7a9 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
" #15 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #16 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #17 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #18 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #19 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #20 pc 00011b77 anonymous:e2796000 (int Main.compare(java.lang.Object, "
@@ -630,16 +661,17 @@
" #21 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #22 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
" #23 pc 000bf7a9 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
" #24 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #25 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #26 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #27 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #28 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #29 pc 00011a29 anonymous:e2796000 (int "
@@ -648,85 +680,90 @@
" #30 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #31 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
" #32 pc 000bf7bb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
" #33 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #34 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #35 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #36 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #37 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #38 pc 0001139b anonymous:e2796000 (boolean Main.foo()+178)\n"
" #39 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #40 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
" #41 pc 000bf7a9 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
" #42 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #43 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #44 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #45 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #46 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #47 pc 00010aa7 anonymous:e2796000 (void Main.runPrimary()+70)\n"
" #48 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #49 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
" #50 pc 000bf7a9 libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+864)\n"
" #51 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #52 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #53 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #54 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #55 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #56 pc 0000ba99 anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
" #57 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #58 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
" #59 pc 000bf7bb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
" #60 pc 00247833 libartd.so "
- "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
- "11ShadowFrameEtPNS_6JValueE+382)\n"
+ "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
+ "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
" #61 pc 0022e935 libartd.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+244)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+244)\n"
" #62 pc 0022f71d libartd.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+128)\n"
" #63 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
" #64 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
" #65 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
" #66 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
" #67 pc 000bf7bb libartd.so "
- "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
+ "const*)+882)\n"
" #68 pc 003b292d libartd.so "
- "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
- "8ArgArrayEPNS_6JValueEPKc+52)\n"
+ "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
+ "art::ArgArray*, art::JValue*, char const*)+52)\n"
" #69 pc 003b26c3 libartd.so "
- "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
- "jmethodIDSt9__va_list+210)\n"
+ "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
+ "_jmethodID*, std::__va_list)+210)\n"
" #70 pc 00308411 libartd.so "
- "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+76)\n"
+ "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
" #71 pc 000e6a9f libartd.so "
- "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDSt9__va_listNS_"
- "9Primitive4TypeENS_10InvokeTypeE+1486)\n"
+ "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
+ "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
" #72 pc 000e19b9 libartd.so "
- "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+40)\n"
+ "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
" #73 pc 0000159f dalvikvm32 "
- "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+30)\n"
+ "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
" #74 pc 00001349 dalvikvm32 (main+896)\n"
" #75 pc 000850c9 libc.so\n",
frame_info);
@@ -884,6 +921,43 @@
EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
}
+struct LeakType {
+ LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
+ : maps(maps), regs(regs), process_memory(process_memory) {}
+
+ Maps* maps;
+ Regs* regs;
+ std::shared_ptr<Memory>& process_memory;
+};
+
+static void OfflineUnwind(void* data) {
+ LeakType* leak_data = reinterpret_cast<LeakType*>(data);
+
+ std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
+ JitDebug jit_debug(leak_data->process_memory);
+ Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
+ unwinder.Unwind();
+ ASSERT_EQ(76U, unwinder.NumFrames());
+}
+
+TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "descriptor1.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ LeakType data(maps_.get(), regs_.get(), process_memory_);
+ TestCheckForLeaks(OfflineUnwind, &data);
+}
+
// The eh_frame_hdr data is present but set to zero fdes. This should
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
@@ -997,41 +1071,44 @@
"(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
" #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
" #03 pc 002657a5 libart.so "
- "(_ZN3art3jit3Jit25MaybeDoOnStackReplacementEPNS_6ThreadEPNS_9ArtMethodEjiPNS_6JValueE+876)\n"
+ "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
+ "art::JValue*)+876)\n"
" #04 pc 004021a7 libart.so (MterpMaybeDoOnStackReplacement+86)\n"
" #05 pc 00412474 libart.so (ExecuteMterpImpl+66164)\n"
" #06 pc cd8365b0 <unknown>\n" // symbol in dex file
" #07 pc 001d7f1b libart.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+374)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+374)\n"
" #08 pc 001dc593 libart.so "
- "(_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameEPNS_6JValueE+154)\n"
+ "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
+ "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
" #09 pc 001f4d01 libart.so "
- "(_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_"
- "11InstructionEtPNS_6JValueE+732)\n"
+ "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
+ "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)\n"
" #10 pc 003fe427 libart.so (MterpInvokeInterface+1354)\n"
" #11 pc 00405b94 libart.so (ExecuteMterpImpl+14740)\n"
" #12 pc 7004873e <unknown>\n" // symbol in dex file
" #13 pc 001d7f1b libart.so "
- "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
- "6JValueEb+374)\n"
+ "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
+ "art::ShadowFrame&, art::JValue, bool)+374)\n"
" #14 pc 001dc4d5 libart.so "
- "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
- "20CodeItemDataAccessorEPNS_11ShadowFrameE+92)\n"
+ "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
+ "const&, art::ShadowFrame*)+92)\n"
" #15 pc 003f25ab libart.so (artQuickToInterpreterBridge+970)\n"
" #16 pc 00417aff libart.so (art_quick_to_interpreter_bridge+30)\n"
" #17 pc 00413575 libart.so (art_quick_invoke_stub_internal+68)\n"
" #18 pc 00418531 libart.so (art_quick_invoke_stub+236)\n"
- " #19 pc 000b468d libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+136)\n"
+ " #19 pc 000b468d libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
+ "int, art::JValue*, char const*)+136)\n"
" #20 pc 00362f49 libart.so "
- "(_ZN3art12_GLOBAL__N_118InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_"
- "9ArtMethodEPNS0_8ArgArrayEPNS_6JValueEPKc+52)\n"
+ "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
+ "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
+ "const*)+52)\n"
" #21 pc 00363cd9 libart.so "
- "(_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_"
- "jobjectP10_jmethodIDP6jvalue+332)\n"
- " #22 pc 003851dd libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
- " #23 pc 00062925 libc.so (_ZL15__pthread_startPv+22)\n"
+ "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
+ "_jobject*, _jmethodID*, jvalue*)+332)\n"
+ " #22 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
+ " #23 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
" #24 pc 0001de39 libc.so (__start_thread+24)\n",
frame_info);
EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
@@ -1107,8 +1184,8 @@
"(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
" #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
- " #03 pc 003851dd libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
- " #04 pc 00062925 libc.so (_ZL15__pthread_startPv+22)\n"
+ " #03 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
+ " #04 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
" #05 pc 0001de39 libc.so (__start_thread+24)\n",
frame_info);
@@ -1135,29 +1212,29 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 0032bfa0 (offset 0x42000) libunwindstack_test (SignalInnerFunction+40)\n"
- " #01 pc 0032bfeb (offset 0x42000) libunwindstack_test (SignalMiddleFunction+2)\n"
- " #02 pc 0032bff3 (offset 0x42000) libunwindstack_test (SignalOuterFunction+2)\n"
- " #03 pc 0032fed3 (offset 0x42000) libunwindstack_test "
- "(_ZN11unwindstackL19SignalCallerHandlerEiP7siginfoPv+26)\n"
- " #04 pc 00026528 (offset 0x25000) libc.so\n"
+ " #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)\n"
+ " #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)\n"
+ " #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
+ " #03 pc 0032fed3 libunwindstack_test "
+ "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
+ " #04 pc 0002652c libc.so (__restore)\n"
" #05 pc 00000000 <unknown>\n"
- " #06 pc 0032c2d9 (offset 0x42000) libunwindstack_test (InnerFunction+736)\n"
- " #07 pc 0032cc4f (offset 0x42000) libunwindstack_test (MiddleFunction+42)\n"
- " #08 pc 0032cc81 (offset 0x42000) libunwindstack_test (OuterFunction+42)\n"
- " #09 pc 0032e547 (offset 0x42000) libunwindstack_test "
- "(_ZN11unwindstackL19RemoteThroughSignalEij+270)\n"
- " #10 pc 0032ed99 (offset 0x42000) libunwindstack_test "
- "(_ZN11unwindstack55UnwindTest_remote_through_signal_with_invalid_func_Test8TestBodyEv+16)\n"
- " #11 pc 00354453 (offset 0x42000) libunwindstack_test (_ZN7testing4Test3RunEv+154)\n"
- " #12 pc 00354de7 (offset 0x42000) libunwindstack_test (_ZN7testing8TestInfo3RunEv+194)\n"
- " #13 pc 00355105 (offset 0x42000) libunwindstack_test (_ZN7testing8TestCase3RunEv+180)\n"
- " #14 pc 0035a215 (offset 0x42000) libunwindstack_test "
- "(_ZN7testing8internal12UnitTestImpl11RunAllTestsEv+664)\n"
- " #15 pc 00359f4f (offset 0x42000) libunwindstack_test (_ZN7testing8UnitTest3RunEv+110)\n"
- " #16 pc 0034d3db (offset 0x42000) libunwindstack_test (main+38)\n"
- " #17 pc 00092c0d (offset 0x25000) libc.so (__libc_init+48)\n"
- " #18 pc 0004202f (offset 0x42000) libunwindstack_test (_start_main+38)\n",
+ " #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
+ " #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
+ " #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)\n"
+ " #09 pc 0032e547 libunwindstack_test "
+ "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
+ " #10 pc 0032ed99 libunwindstack_test "
+ "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
+ " #11 pc 00354453 libunwindstack_test (testing::Test::Run()+154)\n"
+ " #12 pc 00354de7 libunwindstack_test (testing::TestInfo::Run()+194)\n"
+ " #13 pc 00355105 libunwindstack_test (testing::TestCase::Run()+180)\n"
+ " #14 pc 0035a215 libunwindstack_test "
+ "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
+ " #15 pc 00359f4f libunwindstack_test (testing::UnitTest::Run()+110)\n"
+ " #16 pc 0034d3db libunwindstack_test (main+38)\n"
+ " #17 pc 00092c0d libc.so (__libc_init+48)\n"
+ " #18 pc 0004202f libunwindstack_test (_start_main+38)\n",
frame_info);
EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
@@ -1168,7 +1245,7 @@
EXPECT_EQ(0xf43d2ce8U, unwinder.frames()[2].sp);
EXPECT_EQ(0x2e59ed3U, unwinder.frames()[3].pc);
EXPECT_EQ(0xf43d2cf0U, unwinder.frames()[3].sp);
- EXPECT_EQ(0xf4136528U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xf413652cU, unwinder.frames()[4].pc);
EXPECT_EQ(0xf43d2d10U, unwinder.frames()[4].sp);
EXPECT_EQ(0U, unwinder.frames()[5].pc);
EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
@@ -1213,9 +1290,9 @@
EXPECT_EQ(
" #00 pc 0005138c libc.so (__ioctl+8)\n"
" #01 pc 0002140f libc.so (ioctl+30)\n"
- " #02 pc 00039535 libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+204)\n"
- " #03 pc 00039633 libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+10)\n"
- " #04 pc 00039b57 libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+38)\n"
+ " #02 pc 00039535 libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)\n"
+ " #03 pc 00039633 libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
+ " #04 pc 00039b57 libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)\n"
" #05 pc 00000c21 mediaserver (main+104)\n"
" #06 pc 00084b89 libc.so (__libc_init+48)\n"
" #07 pc 00000b77 mediaserver (_start_main+38)\n",
@@ -1239,4 +1316,222 @@
EXPECT_EQ(0xffd4a718U, unwinder.frames()[7].sp);
}
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
+ "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
+ "(ANGLEGetUtilityAPI+56)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+ // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+ // Add the memory that represents the shared library.
+ MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
+ AddMemory(dir_ + "lib_mem.data", memory);
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
+ " #01 pc 000000000005426c linker64 "
+ "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
+ " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
+ " #03 pc 00000000000846f4 libc.so (abort+172)\n"
+ " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
+ " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
+ " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7e82b018c0ULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7df8ca3da0ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7e7eecc6f4ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7dabf3db60ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7e7eeccad4ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7dabf3dc40ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7dabc405b4ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7dabf3dc50ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7e7eec7e68ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7dabf3dc70ULL, unwinder.frames()[6].sp);
+ // Ignore top frame since the test code was modified to end in __libc_init.
+}
+
+TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
+ ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000814bc libc.so (syscall+28)\n"
+ " #01 pc 00000000008cdf5c test.apk (offset 0x5000)\n"
+ " #02 pc 00000000008cde9c test.apk (offset 0x5000)\n"
+ " #03 pc 00000000008cdd70 test.apk (offset 0x5000)\n"
+ " #04 pc 00000000008ce408 test.apk (offset 0x5000)\n"
+ " #05 pc 00000000008ce8d8 test.apk (offset 0x5000)\n"
+ " #06 pc 00000000008ce814 test.apk (offset 0x5000)\n"
+ " #07 pc 00000000008bcf60 test.apk (offset 0x5000)\n"
+ " #08 pc 0000000000133024 test.apk (offset 0x5000)\n"
+ " #09 pc 0000000000134ad0 test.apk (offset 0x5000)\n"
+ " #10 pc 0000000000134b64 test.apk (offset 0x5000)\n"
+ " #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)\n"
+ " #12 pc 0000000000085e18 libc.so (__start_thread+64)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7be6715e9cULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7be4f07800ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7be6715d70ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7be4f07840ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7be6716408ULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7be4f07860ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7be67168d8ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7be4f07880ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7be6716814ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7be4f078f0ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x7be6704f60ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7be4f07910ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x7be5f7b024ULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7be4f07950ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x7be5f7cad0ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7be4f07aa0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x7be5f7cb64ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7be4f07ce0ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x7cbe11406cULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7be4f07d00ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7cbe0b5e18ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7be4f07d20ULL, unwinder.frames()[12].sp);
+}
+
+TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(" #00 pc 00aa7508 invalid.apk (offset 0x12e4000)\n", frame_info);
+ EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
+}
+
+TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
+ ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000e9dd4 libc.so (__write+20)\n"
+ " #01 pc 000000000007ab9c libc.so (_IO_file_write+44)\n"
+ " #02 pc 0000000000079f3e libc.so\n"
+ " #03 pc 000000000007bce8 libc.so (_IO_do_write+24)\n"
+ " #04 pc 000000000007b26e libc.so (_IO_file_xsputn+270)\n"
+ " #05 pc 000000000004f7f9 libc.so (_IO_vfprintf+1945)\n"
+ " #06 pc 0000000000057cb5 libc.so (_IO_printf+165)\n"
+ " #07 pc 0000000000ed1796 perfetto_unittests "
+ "(testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest "
+ "const&, int)+374)\n"
+ " #08 pc 0000000000ed30fd perfetto_unittests "
+ "(testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, "
+ "int)+125)\n"
+ " #09 pc 0000000000ed5e25 perfetto_unittests "
+ "(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
+ " #10 pc 0000000000ef63f3 perfetto_unittests "
+ "(bool "
+ "testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+131)\n"
+ " #11 pc 0000000000ee2a21 perfetto_unittests "
+ "(bool "
+ "testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+ "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+ "const*)+113)\n"
+ " #12 pc 0000000000ed5bb9 perfetto_unittests (testing::UnitTest::Run()+185)\n"
+ " #13 pc 0000000000e900f0 perfetto_unittests (RUN_ALL_TESTS()+16)\n"
+ " #14 pc 0000000000e900d8 perfetto_unittests (main+56)\n"
+ " #15 pc 000000000002352a libc.so (__libc_start_main+234)\n"
+ " #16 pc 0000000000919029 perfetto_unittests (_start+41)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7f9326a57dd4ULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffd224153c8ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7f93269e8b9cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffd224153d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7f93269e7f3eULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffd22415400ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7f93269e9ce8ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffd22415440ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7f93269e926eULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffd22415450ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7f93269bd7f9ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7ffd22415490ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7f93269c5cb5ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7ffd22415a10ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xed1796ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7ffd22415af0ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xed30fdULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7ffd22415b70ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xed5e25ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7ffd22415bb0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xef63f3ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7ffd22415c60ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xee2a21ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7ffd22415cc0ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xed5bb9ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7ffd22415d40ULL, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xe900f0ULL, unwinder.frames()[13].pc);
+ EXPECT_EQ(0x7ffd22415d90ULL, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xe900d8ULL, unwinder.frames()[14].pc);
+ EXPECT_EQ(0x7ffd22415da0ULL, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x7f932699152aULL, unwinder.frames()[15].pc);
+ EXPECT_EQ(0x7ffd22415dd0ULL, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x919029ULL, unwinder.frames()[16].pc);
+ EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index ea992c7..f76a101 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -35,15 +35,23 @@
#include <android-base/threads.h>
#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
#include <unwindstack/Unwinder.h>
+#include "MemoryRemote.h"
#include "TestUtils.h"
namespace unwindstack {
+enum TestTypeEnum : uint8_t {
+ TEST_TYPE_LOCAL_UNWINDER = 0,
+ TEST_TYPE_LOCAL_UNWINDER_FROM_PID,
+ TEST_TYPE_LOCAL_WAIT_FOR_FINISH,
+ TEST_TYPE_REMOTE,
+ TEST_TYPE_REMOTE_WITH_INVALID_CALL,
+};
+
static std::atomic_bool g_ready;
static volatile bool g_ready_for_remote;
static volatile bool g_signal_ready_for_remote;
@@ -72,7 +80,10 @@
extern "C" void SignalInnerFunction() {
g_signal_ready_for_remote = true;
- while (!g_finish.load()) {
+ // Avoid any function calls because not every instruction will be
+ // unwindable.
+ // This method of looping is only used when testing a remote unwind.
+ while (true) {
}
}
@@ -88,10 +99,10 @@
SignalOuterFunction();
}
-static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder* unwinder) {
std::string unwind;
- for (size_t i = 0; i < unwinder.NumFrames(); i++) {
- unwind += unwinder.FormatFrame(i) + '\n';
+ for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+ unwind += unwinder->FormatFrame(i) + '\n';
}
return std::string(
@@ -100,14 +111,10 @@
function_names.front() + "\n" + "Unwind data:\n" + unwind;
}
-static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
- std::vector<const char*> expected_function_names) {
- auto process_memory(Memory::CreateProcessMemory(pid));
+static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
+ unwinder->Unwind();
- Unwinder unwinder(512, maps, regs, process_memory);
- unwinder.Unwind();
-
- for (auto& frame : unwinder.frames()) {
+ for (auto& frame : unwinder->frames()) {
if (frame.function_name == expected_function_names.back()) {
expected_function_names.pop_back();
if (expected_function_names.empty()) {
@@ -119,35 +126,63 @@
ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
}
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+ std::vector<const char*> expected_function_names) {
+ auto process_memory(Memory::CreateProcessMemory(pid));
+
+ Unwinder unwinder(512, maps, regs, process_memory);
+ VerifyUnwind(&unwinder, expected_function_names);
+}
+
// This test assumes that this code is compiled with optimizations turned
// off. If this doesn't happen, then all of the calls will be optimized
// away.
-extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
- if (local) {
- LocalMaps maps;
- ASSERT_TRUE(maps.Parse());
- std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
- RegsGetLocal(regs.get());
-
- VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
- } else {
+extern "C" void InnerFunction(TestTypeEnum test_type) {
+ if (test_type == TEST_TYPE_LOCAL_WAIT_FOR_FINISH) {
+ while (!g_finish.load()) {
+ }
+ return;
+ }
+ if (test_type == TEST_TYPE_REMOTE || test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
g_ready_for_remote = true;
g_ready = true;
- if (trigger_invalid_call) {
+ if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
void (*crash_func)() = nullptr;
crash_func();
}
- while (!g_finish.load()) {
+ // Avoid any function calls because not every instruction will be
+ // unwindable.
+ // This method of looping is only used when testing a remote unwind.
+ while (true) {
}
+ return;
}
+
+ std::unique_ptr<Unwinder> unwinder;
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+ std::unique_ptr<Maps> maps;
+
+ if (test_type == TEST_TYPE_LOCAL_UNWINDER) {
+ maps.reset(new LocalMaps());
+ ASSERT_TRUE(maps->Parse());
+ auto process_memory(Memory::CreateProcessMemory(getpid()));
+ unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
+ } else {
+ UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
+ ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
+ unwinder_from_pid->SetRegs(regs.get());
+ unwinder.reset(unwinder_from_pid);
+ }
+ VerifyUnwind(unwinder.get(), kFunctionOrder);
}
-extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
- InnerFunction(local, trigger_invalid_call);
+extern "C" void MiddleFunction(TestTypeEnum test_type) {
+ InnerFunction(test_type);
}
-extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
- MiddleFunction(local, trigger_invalid_call);
+extern "C" void OuterFunction(TestTypeEnum test_type) {
+ MiddleFunction(test_type);
}
class UnwindTest : public ::testing::Test {
@@ -156,7 +191,26 @@
};
TEST_F(UnwindTest, local) {
- OuterFunction(true, false);
+ OuterFunction(TEST_TYPE_LOCAL_UNWINDER);
+}
+
+TEST_F(UnwindTest, local_use_from_pid) {
+ OuterFunction(TEST_TYPE_LOCAL_UNWINDER_FROM_PID);
+}
+
+static void LocalUnwind(void* data) {
+ TestTypeEnum* test_type = reinterpret_cast<TestTypeEnum*>(data);
+ OuterFunction(*test_type);
+}
+
+TEST_F(UnwindTest, local_check_for_leak) {
+ TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER;
+ TestCheckForLeaks(LocalUnwind, &test_type);
+}
+
+TEST_F(UnwindTest, local_use_from_pid_check_for_leak) {
+ TestTypeEnum test_type = TEST_TYPE_LOCAL_UNWINDER_FROM_PID;
+ TestCheckForLeaks(LocalUnwind, &test_type);
}
void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
@@ -191,7 +245,7 @@
TEST_F(UnwindTest, remote) {
pid_t pid;
if ((pid = fork()) == 0) {
- OuterFunction(false, false);
+ OuterFunction(TEST_TYPE_REMOTE);
exit(0);
}
ASSERT_NE(-1, pid);
@@ -212,11 +266,90 @@
<< "ptrace detach failed with unexpected error: " << strerror(errno);
}
+TEST_F(UnwindTest, unwind_from_pid_remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(TEST_TYPE_REMOTE);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ UnwinderFromPid unwinder(512, pid);
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ VerifyUnwind(&unwinder, kFunctionOrder);
+
+ // Verify that calling the same object works again.
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(TEST_TYPE_REMOTE);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ bool completed;
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+
+ TestCheckForLeaks(unwind_func, &pid);
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
+}
+
+static void RemoteUnwind(void* data) {
+ pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+ RemoteMaps maps(*pid);
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(*pid, &maps, regs.get(), kFunctionOrder);
+}
+
+TEST_F(UnwindTest, remote_check_for_leaks) {
+ RemoteCheckForLeaks(RemoteUnwind);
+}
+
+static void RemoteUnwindFromPid(void* data) {
+ pid_t* pid = reinterpret_cast<pid_t*>(data);
+
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(*pid));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ UnwinderFromPid unwinder(512, *pid);
+ ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ unwinder.SetRegs(regs.get());
+
+ VerifyUnwind(&unwinder, kFunctionOrder);
+}
+
+TEST_F(UnwindTest, remote_unwind_for_pid_check_for_leaks) {
+ RemoteCheckForLeaks(RemoteUnwindFromPid);
+}
+
TEST_F(UnwindTest, from_context) {
std::atomic_int tid(0);
std::thread thread([&]() {
tid = syscall(__NR_gettid);
- OuterFunction(false, false);
+ OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
});
struct sigaction act, oldact;
@@ -266,7 +399,7 @@
act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
ASSERT_EQ(0, sigaction(signal, &act, &oldact));
- OuterFunction(false, signal == SIGSEGV);
+ OuterFunction(signal != SIGSEGV ? TEST_TYPE_REMOTE : TEST_TYPE_REMOTE_WITH_INVALID_CALL);
exit(0);
}
ASSERT_NE(-1, pid);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 831d3b5..ef1950c 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -42,84 +42,99 @@
namespace unwindstack {
-class MapsFake : public Maps {
- public:
- MapsFake() = default;
- virtual ~MapsFake() = default;
-
- bool Parse() { return true; }
-
- void FakeClear() { maps_.clear(); }
-
- void FakeAddMapInfo(MapInfo* map_info) { maps_.push_back(map_info); }
-};
-
class UnwinderTest : public ::testing::Test {
protected:
- static void SetUpTestCase() {
- maps_.FakeClear();
- MapInfo* info =
- new MapInfo(&maps_, 0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so");
+ static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
+ std::string str_name(name);
+ maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ if (elf != nullptr) {
+ const auto& map_info = *--maps_->end();
+ map_info->elf.reset(elf);
+ }
+ }
+
+ static void SetUpTestSuite() {
+ maps_.reset(new Maps);
+
ElfFake* elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
- elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
+ interface_fake->FakeSetBuildID("FAKE");
+ elf->FakeSetInterface(interface_fake);
+ AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
- info = new MapInfo(&maps_, 0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
- info = new MapInfo(&maps_, 0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
- "/dev/fake_device");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x13000, 0x15000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ "/dev/fake_device");
- info = new MapInfo(&maps_, 0x20000, 0x22000, 0, PROT_READ | PROT_WRITE,
- "/system/fake/libunwind.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x20000, 0x22000, 0, PROT_READ | PROT_WRITE, "/system/fake/libunwind.so", elf);
- info = new MapInfo(&maps_, 0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x23000, 0x24000, 0, PROT_READ | PROT_WRITE, "/fake/libanother.so", elf);
- info = new MapInfo(&maps_, 0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
- info = new MapInfo(&maps_, 0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
- elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- maps_.FakeAddMapInfo(info);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetSoname("lib_fake.so");
+ elf->FakeSetInterface(interface);
+ AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
+ MapInfo* map_info = maps_->Find(0x43000);
+ ASSERT_TRUE(map_info != nullptr);
+ map_info->elf_start_offset = 0x1d000;
- info = new MapInfo(&maps_, 0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- info = new MapInfo(&maps_, 0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake.vdex");
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ const auto& info = *--maps_->end();
info->load_bias = 0;
- maps_.FakeAddMapInfo(info);
- info = new MapInfo(&maps_, 0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake_load_bias.so");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
elf->FakeSetLoadBias(0x5000);
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0xa5000, 0xa6000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_load_bias.so",
+ elf);
- info = new MapInfo(&maps_, 0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
- "/fake/fake_offset.oat");
elf = new ElfFake(new MemoryFake);
- info->elf.reset(elf);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- info->elf_offset = 0x8000;
- maps_.FakeAddMapInfo(info);
+ AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
+ elf);
+ const auto& info2 = *--maps_->end();
+ info2->elf_offset = 0x8000;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
+ const auto& info3 = *--maps_->end();
+ info3->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
+ const auto& info4 = *--maps_->end();
+ info4->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
+ const auto& info5 = *--maps_->end();
+ info5->memory_backed_elf = true;
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
+ const auto& info6 = *--maps_->end();
+ info6->memory_backed_elf = true;
+
+ AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+ const auto& info7 = *--maps_->end();
+ info7->load_bias = 0;
process_memory_.reset(new MemoryFake);
}
@@ -130,12 +145,12 @@
regs_.FakeSetReturnAddressValid(false);
}
- static MapsFake maps_;
+ static std::unique_ptr<Maps> maps_;
static RegsFake regs_;
static std::shared_ptr<Memory> process_memory_;
};
-MapsFake UnwinderTest::maps_;
+std::unique_ptr<Maps> UnwinderTest::maps_;
RegsFake UnwinderTest::regs_(5);
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
@@ -150,9 +165,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -164,7 +180,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -178,7 +195,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -192,7 +210,8 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -210,10 +229,11 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -225,7 +245,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -239,7 +260,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -253,7 +275,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -267,9 +290,10 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -281,7 +305,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa5000U, frame->map_start);
EXPECT_EQ(0xa6000U, frame->map_end);
EXPECT_EQ(0x5000U, frame->map_load_bias);
@@ -295,9 +320,10 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -309,7 +335,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa7000U, frame->map_start);
EXPECT_EQ(0xa8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -323,9 +350,41 @@
regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x43000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
+ EXPECT_EQ(0x43000U, frame->map_start);
+ EXPECT_EQ(0x44000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, disable_embedded_soname) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.SetEmbeddedSoname(false);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -337,7 +396,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_offset);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -358,9 +418,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -372,7 +433,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -389,9 +451,10 @@
regs_.set_pc(0x1000);
regs_.set_sp(0x10000);
- Unwinder unwinder(20, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(20, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
@@ -404,7 +467,8 @@
EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
- EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
@@ -429,10 +493,11 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -444,7 +509,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -458,7 +524,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -472,7 +539,7 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -489,9 +556,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -503,7 +571,7 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -517,7 +585,7 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x20000U, frame->map_start);
EXPECT_EQ(0x22000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -536,9 +604,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@@ -555,9 +624,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@@ -569,9 +639,10 @@
regs_.set_pc(0x41000);
regs_.set_sp(0x13000);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -583,7 +654,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -604,9 +676,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -618,7 +691,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -632,7 +706,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -646,7 +721,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x23000U, frame->map_start);
EXPECT_EQ(0x24000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -660,16 +736,68 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
// Fake as if code called a nullptr function.
+ regs_.set_pc(0x20000);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0x10010, false));
+ regs_.FakeSetReturnAddress(0x12);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x20000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added and left if there are only
+// two frames and the pc is in the middle nowhere.
+TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
regs_.set_pc(0);
regs_.set_sp(0x10000);
regs_.FakeSetReturnAddress(0x1202);
regs_.FakeSetReturnAddressValid(true);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
- ASSERT_EQ(1U, unwinder.NumFrames());
+ ASSERT_EQ(2U, unwinder.NumFrames());
auto* frame = &unwinder.frames()[0];
EXPECT_EQ(0U, frame->num);
@@ -679,11 +807,45 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame does not cause a crash when it wasn't
+// really added due to a filter.
+TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
+ regs_.set_pc(0x23000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetReturnAddress(0x23100);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+
+ std::vector<std::string> skip_names{"libanother.so"};
+ unwinder.Unwind(&skip_names);
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(0U, unwinder.NumFrames());
}
// Verify that an unwind stops when a frame is in given suffix.
@@ -700,14 +862,15 @@
ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
// Make sure the elf was not initialized.
- MapInfo* map_info = maps_.Find(0x53000);
+ MapInfo* map_info = maps_->Find(0x53000);
ASSERT_TRUE(map_info != nullptr);
EXPECT_TRUE(map_info->elf == nullptr);
@@ -719,7 +882,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -732,8 +896,9 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_offset);
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
+ EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1d000U, frame->map_exact_offset);
EXPECT_EQ(0x43000U, frame->map_start);
EXPECT_EQ(0x44000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -757,9 +922,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -771,7 +937,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -785,7 +952,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -799,7 +967,8 @@
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -812,9 +981,10 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -826,7 +996,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -840,7 +1011,52 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0xd0400);
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x400U, frame->rel_pc);
+ EXPECT_EQ(0xd0400U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/fake.apk", frame->map_name);
+ EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
+ EXPECT_EQ(0x1000U, frame->map_exact_offset);
+ EXPECT_EQ(0xd0000U, frame->map_start);
+ EXPECT_EQ(0xd1000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -853,9 +1069,10 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0x50000);
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -867,7 +1084,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0U, frame->map_start);
EXPECT_EQ(0U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -881,7 +1099,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -897,9 +1116,10 @@
ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
- Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -911,7 +1131,8 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -925,7 +1146,8 @@
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -939,7 +1161,8 @@
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0x33000U, frame->map_start);
EXPECT_EQ(0x34000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
@@ -952,9 +1175,10 @@
regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
- Unwinder unwinder(1, &maps_, ®s_, process_memory_);
+ Unwinder unwinder(1, maps_.get(), ®s_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -966,15 +1190,144 @@
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
EXPECT_EQ(0xa3000U, frame->map_start);
EXPECT_EQ(0xa4000U, frame->map_end);
EXPECT_EQ(0U, frame->map_load_bias);
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
}
+TEST_F(UnwinderTest, elf_from_memory_not_file) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc0050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_TRUE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc0050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/fake/unreadable.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc0000U, frame->map_start);
+ EXPECT_EQ(0xc1000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc1050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc1050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("[vdso]", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc1000U, frame->map_start);
+ EXPECT_EQ(0xc2000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc2050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc2050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc2000U, frame->map_start);
+ EXPECT_EQ(0xc3000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.set_pc(0xc3050);
+ regs_.set_sp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50U, frame->rel_pc);
+ EXPECT_EQ(0xc3050U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0xc3000U, frame->map_start);
+ EXPECT_EQ(0xc4000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+}
+
// Verify format frame code.
-TEST_F(UnwinderTest, format_frame_static) {
+TEST_F(UnwinderTest, format_frame) {
+ RegsFake regs_arm(10);
+ regs_arm.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder32(10, maps_.get(), ®s_arm, process_memory_);
+
+ RegsFake regs_arm64(10);
+ regs_arm64.FakeSetArch(ARCH_ARM64);
+ Unwinder unwinder64(10, maps_.get(), ®s_arm64, process_memory_);
+
FrameData frame;
frame.num = 1;
frame.rel_pc = 0x1000;
@@ -983,39 +1336,67 @@
frame.function_name = "function";
frame.function_offset = 100;
frame.map_name = "/fake/libfake.so";
- frame.map_offset = 0x2000;
+ frame.map_elf_start_offset = 0x2000;
frame.map_start = 0x3000;
frame.map_end = 0x6000;
frame.map_flags = PROT_READ;
- EXPECT_EQ(" #01 pc 0000000000001000 (offset 0x2000) /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 (offset 0x2000) /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
+ unwinder32.FormatFrame(frame));
- frame.map_offset = 0;
+ frame.map_elf_start_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)",
- Unwinder::FormatFrame(frame, true));
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
frame.function_offset = 0;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)",
- Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+ unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", unwinder32.FormatFrame(frame));
+
+ // Verify the function name is demangled.
+ frame.function_name = "_ZN4funcEv";
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", unwinder32.FormatFrame(frame));
frame.function_name = "";
- EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", unwinder32.FormatFrame(frame));
frame.map_name = "";
- EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", unwinder32.FormatFrame(frame));
frame.map_start = 0;
frame.map_end = 0;
- EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", Unwinder::FormatFrame(frame, false));
- EXPECT_EQ(" #01 pc 00001000 <unknown>", Unwinder::FormatFrame(frame, true));
+ EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", unwinder64.FormatFrame(frame));
+ EXPECT_EQ(" #01 pc 00001000 <unknown>", unwinder32.FormatFrame(frame));
+}
+
+TEST_F(UnwinderTest, format_frame_build_id) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), ®s, process_memory_);
+
+ FrameData frame;
+ frame.num = 1;
+ frame.rel_pc = 0x1000;
+ frame.pc = 0x4000;
+ frame.sp = 0x1000;
+ frame.function_name = "function";
+ frame.function_offset = 100;
+ frame.map_name = "/fake/libfake.so";
+ frame.map_elf_start_offset = 0;
+ frame.map_start = 0x3000;
+ frame.map_end = 0x6000;
+ frame.map_flags = PROT_READ;
+
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
+ unwinder.SetDisplayBuildID(true);
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100) (BuildId: 46414b45)",
+ unwinder.FormatFrame(frame));
}
static std::string ArchToString(ArchEnum arch) {
@@ -1033,7 +1414,7 @@
}
// Verify format frame code.
-TEST_F(UnwinderTest, format_frame) {
+TEST_F(UnwinderTest, format_frame_by_arch) {
std::vector<Regs*> reg_list;
RegsArm* arm = new RegsArm;
arm->set_pc(0x2300);
@@ -1068,7 +1449,7 @@
for (auto regs : reg_list) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
- Unwinder unwinder(64, &maps_, regs, process_memory_);
+ Unwinder unwinder(64, maps_.get(), regs, process_memory_);
unwinder.Unwind();
ASSERT_EQ(1U, unwinder.NumFrames());
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
new file mode 100644
index 0000000..022404c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
@@ -0,0 +1 @@
+c7ee8000-c8c52fff r-xp 12e4000 00:00 0 invalid.apk
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
new file mode 100644
index 0000000..b7f10ef
--- /dev/null
+++ b/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: c0434c00
+r1: 2a4c9fbc
+r2: 00000000
+r3: c83ef1f9
+r4: 00000004
+r5: c2044904
+r6: 00000000
+r7: c20443b8
+r8: 000b33ff
+r9: c20444b0
+r10: cac90740
+r11: 00000000
+ip: ed891ca4
+sp: c2044218
+lr: ed807265
+pc: c898f508
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
index 3cd9d40..4043122 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
@@ -1,5 +1,6 @@
ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
+e0445000-e0447000 r--p 0 00:00 0 137-cfi.odex
e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
e648e000-e690f000 r-xp 0 00:00 0 libart.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
index a8d215c..f255a44 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
+++ b/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
@@ -1,5 +1,6 @@
56573000-56577000 r-xp 0 00:00 0 dalvikvm32
eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec604000-ec606000 r--p 0 00:00 0 137-cfi.odex
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
new file mode 100644
index 0000000..63383d0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
new file mode 100644
index 0000000..ba5a31b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
@@ -0,0 +1,3 @@
+200000-919000 r--p 0 00:00 0 perfetto_unittests
+919000-1a0c000 r-xp 719000 00:00 0 perfetto_unittests
+7f932696e000-7f9326b23000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
new file mode 100644
index 0000000..a30e599
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
new file mode 100644
index 0000000..6cb4055
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
@@ -0,0 +1,17 @@
+rax: 3b
+rbx: 3b
+rcx: 7f9326a57dd4
+rdx: 3b
+r8: 7ffd22415b09
+r9: 7ffd224155e0
+r10: 0
+r11: 246
+r12: 7f9326d28760
+r13: 3b
+r14: 7f9326d23760
+r15: 3b
+rdi: 1
+rsi: 2678850
+rbp: 2678850
+rsp: 7ffd224153c8
+rip: 7f9326a57dd4
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
new file mode 100644
index 0000000..4edfe07
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
index 6224464..768dd9f 100644
--- a/libunwindstack/tests/files/offline/offset_arm/maps.txt
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -1,2 +1,4 @@
+2b2a000-2b6c000 r--p 0 00:00 0 libunwindstack_test
2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4110000-f4135000 r--p 0 00:00 0 libc.so
f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
new file mode 100644
index 0000000..0277359
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
new file mode 100644
index 0000000..c4fc067
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 4000 00:00 0 ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 40000 00:00 0 ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0 libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0 libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0 vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0 linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
new file mode 100644
index 0000000..f39d127
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
new file mode 100644
index 0000000..20008fd
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
new file mode 100644
index 0000000..b90933b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
new file mode 100644
index 0000000..386d57a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
@@ -0,0 +1,7 @@
+7dabc03000-7dabc3f000 r--p 21d5000 00:00 0 ANGLEPrebuilt.apk
+7dabc3f000-7dabcf0000 r-xp 2211000 00:00 0 ANGLEPrebuilt.apk
+7e7ee48000-7e7ee88000 r--p 0 00:00 0 libc.so
+7e7ee88000-7e7ef32000 r-xp 40000 00:00 0 libc.so
+7e82b01000-7e82b03000 r-xp 0 00:00 0 vdso.so
+7e82b03000-7e82b3c000 r--p 0 00:00 0 linker64
+7e82b3c000-7e82c77000 r-xp 39000 00:00 0 linker64
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
new file mode 100644
index 0000000..1e2ea32
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7df8ca3c24
+x1: 0
+x2: ffffffff
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 20dd5829922a93ac
+x10: 7e82b57420
+x11: 4100
+x12: 7df8ca3b70
+x13: 7df8ca3b98
+x14: 73d015e5
+x15: 39a36122467299
+x16: 76ac
+x17: 0
+x18: 7df8cfc000
+x19: 7dabf3e7a0
+x20: 7df8ca3da0
+x21: 59616d61
+x22: 1
+x23: 7df8ca3c24
+x24: 1894
+x25: 62
+x26: 2
+x27: 0
+x28: 7dabf3e790
+x29: 7df8ca3d90
+sp: 7df8ca3bf0
+lr: 7e82b57270
+pc: 7e82c4fcbc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
new file mode 100644
index 0000000..ec07e15
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
new file mode 100644
index 0000000..825bb1a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
new file mode 100644
index 0000000..205ebd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
new file mode 100644
index 0000000..cac1dd9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
new file mode 100644
index 0000000..2c5ca62
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
@@ -0,0 +1,3 @@
+7be5e48000-7be6b2b000 r-xp 5000 00:00 0 test.apk
+7cbe030000-7cbe070000 r--p 0 00:00 0 libc.so
+7cbe070000-7cbe11a000 r-xp 40000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
new file mode 100644
index 0000000..090aeda
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
@@ -0,0 +1,33 @@
+x0: 7c326f6568
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 62
+x9: 1
+x10: 1
+x11: 0
+x12: ffffffffc4653600
+x13: 17645696f
+x14: 2742ed97ca77a3
+x15: 3ab49084
+x16: 7be6b6bdb8
+x17: 7cbe0b14a0
+x18: 7c2b02a000
+x19: 0
+x20: 7c326f6568
+x21: 7be69c827c
+x22: 7be69c8272
+x23: 1
+x24: 7be74f7100
+x25: 881
+x26: 7be4f07a00
+x27: c479c000
+x28: 7be4f07998
+x29: 7be4f079b4
+sp: 7be4f077d0
+lr: 7be6715f60
+pc: 7cbe0b14bc
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
new file mode 100644
index 0000000..27d5bf3
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
new file mode 100644
index 0000000..70a9c71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 22ca7bf..1812e50 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -35,7 +35,12 @@
#include <unwindstack/Unwinder.h>
static bool Attach(pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+ ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
}
@@ -52,12 +57,6 @@
}
void DoUnwind(pid_t pid) {
- unwindstack::RemoteMaps remote_maps(pid);
- if (!remote_maps.Parse()) {
- printf("Failed to parse map data.\n");
- return;
- }
-
unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
if (regs == nullptr) {
printf("Unable to get remote reg data\n");
@@ -90,15 +89,13 @@
}
printf("\n");
- auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+ unwindstack::UnwinderFromPid unwinder(1024, pid);
+ if (!unwinder.Init(regs->Arch())) {
+ printf("Failed to init unwinder object.\n");
+ return;
+ }
- unwindstack::JitDebug jit_debug(process_memory);
- unwinder.SetJitDebug(&jit_debug, regs->Arch());
-
- unwindstack::DexFiles dex_files(process_memory);
- unwinder.SetDexFiles(&dex_files, regs->Arch());
-
+ unwinder.SetRegs(regs);
unwinder.Unwind();
// Print the frames.
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 640992f..4f67d67 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
@@ -46,11 +47,17 @@
uint64_t start;
uint64_t end;
uint64_t offset;
+ uint64_t flags;
std::string name;
};
static bool Attach(pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
+ ptrace(PTRACE_DETACH, pid, 0, 0);
return false;
}
@@ -158,14 +165,19 @@
return true;
}
-bool CopyElfFromFile(map_info_t* info) {
+bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
+ std::string cur_name = basename(info->name.c_str());
+ if (*file_copied) {
+ info->name = cur_name;
+ return true;
+ }
+
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(info->name.c_str(), "r"), &fclose);
if (fp == nullptr) {
perror((std::string("Cannot open ") + info->name).c_str());
return false;
}
- std::string cur_name = basename(info->name.c_str());
std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
if (output == nullptr) {
perror((std::string("Cannot create file " + cur_name)).c_str());
@@ -188,6 +200,39 @@
return true;
}
+map_info_t* FillInAndGetMapInfo(std::unordered_map<uint64_t, map_info_t>& maps_by_start,
+ unwindstack::MapInfo* map_info) {
+ auto info = &maps_by_start[map_info->start];
+ info->start = map_info->start;
+ info->end = map_info->end;
+ info->offset = map_info->offset;
+ info->name = map_info->name;
+ info->flags = map_info->flags;
+
+ return info;
+}
+
+void SaveMapInformation(std::shared_ptr<unwindstack::Memory>& process_memory, map_info_t* info,
+ bool* file_copied) {
+ if (CopyElfFromFile(info, file_copied)) {
+ return;
+ }
+ *file_copied = false;
+
+ // Try to create the elf from memory, this will handle cases where
+ // the data only exists in memory such as vdso data on x86.
+ if (CreateElfFromMemory(process_memory, info)) {
+ return;
+ }
+
+ printf("Cannot save memory or file for map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
+}
+
int SaveData(pid_t pid) {
unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
if (regs == nullptr) {
@@ -195,12 +240,6 @@
return 1;
}
- unwindstack::RemoteMaps maps(pid);
- if (!maps.Parse()) {
- printf("Unable to parse maps.\n");
- return 1;
- }
-
// Save the current state of the registers.
if (!SaveRegs(regs)) {
return 1;
@@ -208,46 +247,48 @@
// Do an unwind so we know how much of the stack to save, and what
// elf files are involved.
+ unwindstack::UnwinderFromPid unwinder(1024, pid);
+ if (!unwinder.Init(regs->Arch())) {
+ printf("Unable to init unwinder object.\n");
+ return 1;
+ }
+ unwinder.SetRegs(regs);
uint64_t sp = regs->sp();
- auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- unwindstack::JitDebug jit_debug(process_memory);
- unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
- unwinder.SetJitDebug(&jit_debug, regs->Arch());
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
std::vector<std::pair<uint64_t, uint64_t>> stacks;
+ unwindstack::Maps* maps = unwinder.GetMaps();
uint64_t sp_map_start = 0;
- unwindstack::MapInfo* map_info = maps.Find(sp);
+ unwindstack::MapInfo* map_info = maps->Find(sp);
if (map_info != nullptr) {
stacks.emplace_back(std::make_pair(sp, map_info->end));
sp_map_start = map_info->start;
}
- for (auto frame : unwinder.frames()) {
- map_info = maps.Find(frame.sp);
+ for (const auto& frame : unwinder.frames()) {
+ map_info = maps->Find(frame.sp);
if (map_info != nullptr && sp_map_start != map_info->start) {
stacks.emplace_back(std::make_pair(frame.sp, map_info->end));
sp_map_start = map_info->start;
}
if (maps_by_start.count(frame.map_start) == 0) {
- auto info = &maps_by_start[frame.map_start];
- info->start = frame.map_start;
- info->end = frame.map_end;
- info->offset = frame.map_offset;
- info->name = frame.map_name;
- if (!CopyElfFromFile(info)) {
- // Try to create the elf from memory, this will handle cases where
- // the data only exists in memory such as vdso data on x86.
- if (!CreateElfFromMemory(process_memory, info)) {
- printf("Ignoring map ");
- if (!info->name.empty()) {
- printf("%s\n", info->name.c_str());
- } else {
- printf("anonymous:%" PRIx64 "\n", info->start);
- }
- }
+ map_info = maps->Find(frame.map_start);
+
+ auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+ bool file_copied = false;
+ SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
+
+ // If you are using a a linker that creates two maps (one read-only, one
+ // read-executable), it's necessary to capture the previous map
+ // information if needed.
+ unwindstack::MapInfo* prev_map = map_info->prev_map;
+ if (prev_map != nullptr && map_info->offset != 0 && prev_map->offset == 0 &&
+ prev_map->flags == PROT_READ && map_info->name == prev_map->name &&
+ maps_by_start.count(prev_map->start) == 0) {
+ info = FillInAndGetMapInfo(maps_by_start, prev_map);
+ SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
}
}
}
@@ -272,8 +313,18 @@
}
for (auto& element : sorted_maps) {
+ char perms[5] = {"---p"};
map_info_t& map = element.second;
- fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " r-xp %" PRIx64 " 00:00 0", map.start, map.end,
+ if (map.flags & PROT_READ) {
+ perms[0] = 'r';
+ }
+ if (map.flags & PROT_WRITE) {
+ perms[1] = 'w';
+ }
+ if (map.flags & PROT_EXEC) {
+ perms[2] = 'x';
+ }
+ fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
map.offset);
if (!map.name.empty()) {
fprintf(fp.get(), " %s", map.name.c_str());
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index aebeb95..7a6d8ba 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -31,6 +31,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
#include "ArmExidx.h"
#include "ElfInterfaceArm.h"
@@ -44,10 +45,10 @@
}
printf("ARM Unwind Information:\n");
+ uint64_t load_bias = elf->GetLoadBias();
for (const auto& entry : interface->pt_loads()) {
- uint64_t load_bias = entry.second.table_offset;
printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
- entry.second.table_size + load_bias);
+ entry.second.offset + entry.second.table_size + load_bias);
for (auto pc : *interface) {
std::string name;
printf(" PC 0x%" PRIx64, pc + load_bias);
@@ -105,24 +106,26 @@
// Send all log messages to stdout.
log_to_stdout(true);
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(file, offset)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
+ Elf elf(Memory::CreateFileMemory(file, offset).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n", soname.c_str());
}
+ std::string build_id = elf.GetBuildID();
+ if (!build_id.empty()) {
+ printf("Build ID: ");
+ for (size_t i = 0; i < build_id.size(); ++i) {
+ printf("%02hhx", build_id[i]);
+ }
+ printf("\n");
+ }
+
ElfInterface* interface = elf.interface();
if (elf.machine_type() == EM_ARM) {
DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 4b6f49a..d0562d9 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -33,6 +33,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
#include <unwindstack/Log.h>
+#include <unwindstack/Memory.h>
#include "ArmExidx.h"
#include "DwarfOp.h"
@@ -165,14 +166,7 @@
}
int GetInfo(const char* file, uint64_t pc) {
- MemoryFileAtOffset* memory = new MemoryFileAtOffset;
- if (!memory->Init(file, 0)) {
- // Initializatation failed.
- printf("Failed to init\n");
- return 1;
- }
-
- Elf elf(memory);
+ Elf elf(Memory::CreateFileMemory(file, pc).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
return 1;
@@ -185,8 +179,8 @@
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
@@ -205,7 +199,7 @@
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
- PrintRegInformation(section, memory, pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
} else {
printf("\nno eh_frame information\n");
}
@@ -213,7 +207,7 @@
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
- PrintRegInformation(section, memory, pc, elf.class_type());
+ PrintRegInformation(section, elf.memory(), pc, elf.class_type());
printf("\n");
} else {
printf("\nno debug_frame information\n");
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 9128430..8df2284 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -59,20 +59,14 @@
// Send all log messages to stdout.
unwindstack::log_to_stdout(true);
- unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
- if (!memory->Init(argv[1], 0)) {
- printf("Failed to init\n");
- return 1;
- }
-
- unwindstack::Elf elf(memory);
+ unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", argv[1]);
return 1;
}
- std::string soname;
- if (elf.GetSoname(&soname)) {
+ std::string soname(elf.GetSoname());
+ if (!soname.empty()) {
printf("Soname: %s\n\n", soname.c_str());
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 600c91c..8be4dd0 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,16 +17,19 @@
vendor_available: true,
recovery_available: true,
host_supported: true,
+ native_bridge_supported: true,
header_libs: [
"liblog_headers",
"libsystem_headers",
"libcutils_headers",
+ "libprocessgroup_headers",
],
export_header_lib_headers: [
"liblog_headers",
"libsystem_headers",
"libcutils_headers",
+ "libprocessgroup_headers",
],
export_include_dirs: ["include"],
@@ -67,6 +70,7 @@
],
shared_libs: [
+ "libcutils",
"liblog",
],
@@ -81,7 +85,7 @@
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libcutils",
+ "libprocessgroup",
"libdl",
"libvndksupport",
],
@@ -95,14 +99,6 @@
exclude_shared_libs: ["libvndksupport"],
},
- host: {
- cflags: ["-DLIBUTILS_NATIVE=1"],
-
- shared: {
- enabled: false,
- },
- },
-
linux_bionic: {
enabled: true,
},
@@ -126,6 +122,7 @@
cc_library {
name: "libutils",
defaults: ["libutils_defaults"],
+ native_bridge_supported: true,
srcs: [
"FileMap.cpp",
@@ -155,7 +152,7 @@
],
},
linux: {
- shared_libs: ["libbase"],
+ header_libs: ["libbase_headers"],
srcs: [
"Looper.cpp",
],
@@ -177,32 +174,118 @@
},
},
+ shared_libs: [
+ "libutils",
+ "libbacktrace",
+ ],
+
target: {
- android: {
- shared_libs: [
- "libutils",
- "libbacktrace",
- ],
- },
linux: {
srcs: [
"ProcessCallStack.cpp",
],
},
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
},
}
-// Include subdirectory makefiles
-// ============================================================
+cc_test {
+ name: "libutils_test",
+ host_supported: true,
+
+ srcs: [
+ "BitSet_test.cpp",
+ "FileMap_test.cpp",
+ "LruCache_test.cpp",
+ "Mutex_test.cpp",
+ "SharedBuffer_test.cpp",
+ "String8_test.cpp",
+ "StrongPointer_test.cpp",
+ "Unicode_test.cpp",
+ "Vector_test.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "SystemClock_test.cpp",
+ ],
+ shared_libs: [
+ "libz",
+ "liblog",
+ "libcutils",
+ "libutils",
+ "libbase",
+ ],
+ },
+ linux: {
+ srcs: [
+ "Looper_test.cpp",
+ "RefBase_test.cpp",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libutils",
+ "liblog",
+ "libbase",
+ ],
+ },
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wthread-safety",
+ ],
+
+ test_suites: ["device-tests"],
+}
+
+// TODO: the test infrastructure isn't yet capable of running this,
+// so it's broken out into its own test so that the main libutils_tests
+// can be in presubmit even if this can't.
cc_test {
- name: "SharedBufferTest",
- host_supported: true,
- static_libs: ["libutils"],
- shared_libs: ["liblog"],
- srcs: ["SharedBufferTest.cpp"],
+ name: "libutils_singleton_test",
+ srcs: ["Singleton_test.cpp"],
cflags: [
"-Wall",
"-Werror",
],
+ shared_libs: ["libbase"],
+
+ required: [
+ ":libutils_test_singleton1",
+ ":libutils_test_singleton2",
+ ],
+}
+
+cc_test_library {
+ name: "libutils_test_singleton1",
+ host_supported: true,
+ relative_install_path: "libutils_test",
+ srcs: ["Singleton_test1.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test_library {
+ name: "libutils_test_singleton2",
+ host_supported: true,
+ relative_install_path: "libutils_test",
+ srcs: ["Singleton_test2.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: ["libutils_test_singleton1"],
}
diff --git a/libutils/tests/BitSet_test.cpp b/libutils/BitSet_test.cpp
similarity index 100%
rename from libutils/tests/BitSet_test.cpp
rename to libutils/BitSet_test.cpp
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 5feb2aa..1202c15 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -174,12 +174,6 @@
return false;
}
#else // !defined(__MINGW32__)
- int prot, flags, adjust;
- off64_t adjOffset;
- size_t adjLength;
-
- void* ptr;
-
assert(fd >= 0);
assert(offset >= 0);
assert(length > 0);
@@ -193,20 +187,23 @@
}
}
- adjust = offset % mPageSize;
- adjOffset = offset - adjust;
- adjLength = length + adjust;
+ int adjust = offset % mPageSize;
+ off64_t adjOffset = offset - adjust;
+ size_t adjLength = length + adjust;
- flags = MAP_SHARED;
- prot = PROT_READ;
- if (!readOnly)
- prot |= PROT_WRITE;
+ int flags = MAP_SHARED;
+ int prot = PROT_READ;
+ if (!readOnly) prot |= PROT_WRITE;
- ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
+ void* ptr = mmap(nullptr, adjLength, prot, flags, fd, adjOffset);
if (ptr == MAP_FAILED) {
- ALOGE("mmap(%lld,%zu) failed: %s\n",
- (long long)adjOffset, adjLength, strerror(errno));
- return false;
+ if (errno == EINVAL && length == 0) {
+ ptr = nullptr;
+ adjust = 0;
+ } else {
+ ALOGE("mmap(%lld,%zu) failed: %s\n", (long long)adjOffset, adjLength, strerror(errno));
+ return false;
+ }
}
mBasePtr = ptr;
#endif // !defined(__MINGW32__)
@@ -217,8 +214,6 @@
mDataPtr = (char*) mBasePtr + adjust;
mDataLength = length;
- assert(mBasePtr != NULL);
-
ALOGV("MAP: base %p/%zu data %p/%zu\n",
mBasePtr, mBaseLength, mDataPtr, mDataLength);
diff --git a/libutils/FileMap_test.cpp b/libutils/FileMap_test.cpp
new file mode 100644
index 0000000..576d89b
--- /dev/null
+++ b/libutils/FileMap_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/FileMap.h"
+
+#include <gtest/gtest.h>
+
+#include "android-base/file.h"
+
+TEST(FileMap, zero_length_mapping) {
+ // http://b/119818070 "app crashes when reading asset of zero length".
+ // mmap fails with EINVAL for a zero length region.
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ android::FileMap m;
+ ASSERT_TRUE(m.create("test", tf.fd, 4096, 0, true));
+ ASSERT_STREQ("test", m.getFileName());
+ ASSERT_EQ(0u, m.getDataLength());
+ ASSERT_EQ(4096, m.getDataOffset());
+}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 102fdf0..14e3e35 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -14,7 +14,9 @@
#define DEBUG_CALLBACKS 0
#include <utils/Looper.h>
+
#include <sys/eventfd.h>
+#include <cinttypes>
namespace android {
@@ -51,9 +53,6 @@
// --- Looper ---
-// Hint for number of file descriptors to be associated with the epoll instance.
-static const int EPOLL_SIZE_HINT = 8;
-
// Maximum number of file descriptors for which to retrieve poll events each iteration.
static const int EPOLL_MAX_EVENTS = 16;
@@ -79,8 +78,8 @@
}
void Looper::initTLSKey() {
- int result = pthread_key_create(& gTLSKey, threadDestructor);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+ int error = pthread_key_create(&gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
}
void Looper::threadDestructor(void *st) {
@@ -139,7 +138,7 @@
}
// Allocate the new epoll instance and register the wake pipe.
- mEpollFd.reset(epoll_create(EPOLL_SIZE_HINT));
+ mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
@@ -402,8 +401,8 @@
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
- LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
- strerror(errno));
+ LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
+ mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
diff --git a/libutils/tests/Looper_test.cpp b/libutils/Looper_test.cpp
similarity index 96%
rename from libutils/tests/Looper_test.cpp
rename to libutils/Looper_test.cpp
index 2282ced..6fdc0ed 100644
--- a/libutils/tests/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -9,7 +9,7 @@
#include <unistd.h>
#include <time.h>
-#include "TestHelpers.h"
+#include <utils/threads.h>
// # of milliseconds to fudge stopwatch measurements
#define TIMING_TOLERANCE_MS 25
@@ -23,6 +23,59 @@
MSG_TEST4 = 4,
};
+class Pipe {
+public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
+
+class DelayedTask : public Thread {
+ int mDelayMillis;
+
+public:
+ explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+
+protected:
+ virtual ~DelayedTask() { }
+
+ virtual void doTask() = 0;
+
+ virtual bool threadLoop() {
+ usleep(mDelayMillis * 1000);
+ doTask();
+ return false;
+ }
+};
+
class DelayedWake : public DelayedTask {
sp<Looper> mLooper;
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/LruCache_test.cpp
similarity index 100%
rename from libutils/tests/LruCache_test.cpp
rename to libutils/LruCache_test.cpp
diff --git a/libutils/tests/Mutex_test.cpp b/libutils/Mutex_test.cpp
similarity index 78%
rename from libutils/tests/Mutex_test.cpp
rename to libutils/Mutex_test.cpp
index 8a1805f..79f4302 100644
--- a/libutils/tests/Mutex_test.cpp
+++ b/libutils/Mutex_test.cpp
@@ -29,4 +29,20 @@
android::Mutex::Autolock _l(mLock);
i = 0;
modifyLockedVariable();
-}
\ No newline at end of file
+}
+
+TEST(Mutex, tryLock) {
+ if (mLock.tryLock() != 0) {
+ return;
+ }
+ mLock.unlock();
+}
+
+#if defined(__ANDROID__)
+TEST(Mutex, timedLock) {
+ if (mLock.timedLock(1) != 0) {
+ return;
+ }
+ mLock.unlock();
+}
+#endif
diff --git a/libutils/tests/RefBase_test.cpp b/libutils/RefBase_test.cpp
similarity index 64%
rename from libutils/tests/RefBase_test.cpp
rename to libutils/RefBase_test.cpp
index 2e0cf6e..c9b4894 100644
--- a/libutils/tests/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -45,6 +45,44 @@
bool* mDeleted;
};
+// A version of Foo that ensures that all objects are allocated at the same
+// address. No more than one can be allocated at a time. Thread-hostile.
+class FooFixedAlloc : public RefBase {
+public:
+ static void* operator new(size_t size) {
+ if (mAllocCount != 0) {
+ abort();
+ }
+ mAllocCount = 1;
+ if (theMemory == nullptr) {
+ theMemory = malloc(size);
+ }
+ return theMemory;
+ }
+
+ static void operator delete(void *p) {
+ if (mAllocCount != 1 || p != theMemory) {
+ abort();
+ }
+ mAllocCount = 0;
+ }
+
+ FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {
+ *mDeleted = false;
+ }
+
+ ~FooFixedAlloc() {
+ *mDeleted = true;
+ }
+private:
+ bool* mDeleted;
+ static int mAllocCount;
+ static void* theMemory;
+};
+
+int FooFixedAlloc::mAllocCount(0);
+void* FooFixedAlloc::theMemory(nullptr);
+
TEST(RefBase, StrongMoves) {
bool isDeleted;
Foo* foo = new Foo(&isDeleted);
@@ -90,6 +128,118 @@
ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
}
+TEST(RefBase, Comparisons) {
+ bool isDeleted, isDeleted2, isDeleted3;
+ Foo* foo = new Foo(&isDeleted);
+ Foo* foo2 = new Foo(&isDeleted2);
+ sp<Foo> sp1(foo);
+ sp<Foo> sp2(foo2);
+ wp<Foo> wp1(sp1);
+ wp<Foo> wp2(sp1);
+ wp<Foo> wp3(sp2);
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_TRUE(wp1 == sp1);
+ ASSERT_TRUE(wp3 == sp2);
+ ASSERT_TRUE(wp1 != sp2);
+ ASSERT_TRUE(wp1 <= wp2);
+ ASSERT_TRUE(wp1 >= wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ ASSERT_FALSE(wp1 > wp2);
+ ASSERT_FALSE(wp1 < wp2);
+ ASSERT_FALSE(sp1 == sp2);
+ ASSERT_TRUE(sp1 != sp2);
+ bool sp1_smaller = sp1 < sp2;
+ wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;
+ wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ sp2 = nullptr;
+ ASSERT_TRUE(isDeleted2);
+ ASSERT_FALSE(isDeleted);
+ ASSERT_FALSE(wp3 == sp2);
+ // Comparison results on weak pointers should not be affected.
+ ASSERT_TRUE(wp_smaller < wp_larger);
+ ASSERT_TRUE(wp_smaller != wp_larger);
+ ASSERT_TRUE(wp_smaller <= wp_larger);
+ ASSERT_FALSE(wp_smaller == wp_larger);
+ ASSERT_FALSE(wp_smaller > wp_larger);
+ ASSERT_FALSE(wp_smaller >= wp_larger);
+ wp2 = nullptr;
+ ASSERT_FALSE(wp1 == wp2);
+ ASSERT_TRUE(wp1 != wp2);
+ wp1.clear();
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+ wp3.clear();
+ ASSERT_TRUE(wp1 == wp3);
+ ASSERT_FALSE(wp1 != wp3);
+ ASSERT_FALSE(isDeleted);
+ sp1.clear();
+ ASSERT_TRUE(isDeleted);
+ ASSERT_TRUE(sp1 == sp2);
+ // Try to check that null pointers are properly initialized.
+ {
+ // Try once with non-null, to maximize chances of getting junk on the
+ // stack.
+ sp<Foo> sp3(new Foo(&isDeleted3));
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_FALSE(wp4 == wp5);
+ ASSERT_TRUE(wp4 != wp5);
+ ASSERT_FALSE(sp3 == wp5);
+ ASSERT_FALSE(wp5 == sp3);
+ ASSERT_TRUE(sp3 != wp5);
+ ASSERT_TRUE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+ {
+ sp<Foo> sp3;
+ wp<Foo> wp4(sp3);
+ wp<Foo> wp5;
+ ASSERT_TRUE(wp4 == wp5);
+ ASSERT_FALSE(wp4 != wp5);
+ ASSERT_TRUE(sp3 == wp5);
+ ASSERT_TRUE(wp5 == sp3);
+ ASSERT_FALSE(sp3 != wp5);
+ ASSERT_FALSE(wp5 != sp3);
+ ASSERT_TRUE(sp3 == wp4);
+ }
+}
+
+// Check whether comparison against dead wp works, even if the object referenced
+// by the new wp happens to be at the same address.
+TEST(RefBase, ReplacedComparison) {
+ bool isDeleted, isDeleted2;
+ FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);
+ sp<FooFixedAlloc> sp1(foo);
+ wp<FooFixedAlloc> wp1(sp1);
+ ASSERT_TRUE(wp1 == sp1);
+ sp1.clear(); // Deallocates the object.
+ ASSERT_TRUE(isDeleted);
+ FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);
+ ASSERT_FALSE(isDeleted2);
+ ASSERT_EQ(foo, foo2); // Not technically a legal comparison, but ...
+ sp<FooFixedAlloc> sp2(foo2);
+ wp<FooFixedAlloc> wp2(sp2);
+ ASSERT_TRUE(sp2 == wp2);
+ ASSERT_FALSE(sp2 != wp2);
+ ASSERT_TRUE(sp2 != wp1);
+ ASSERT_FALSE(sp2 == wp1);
+ ASSERT_FALSE(sp2 == sp1); // sp1 is null.
+ ASSERT_FALSE(wp1 == wp2); // wp1 refers to old object.
+ ASSERT_TRUE(wp1 != wp2);
+ ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);
+ ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);
+ ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);
+ ASSERT_FALSE(wp1 == nullptr);
+ wp1 = sp2;
+ ASSERT_TRUE(wp1 == wp2);
+ ASSERT_FALSE(wp1 != wp2);
+}
// Set up a situation in which we race with visit2AndRremove() to delete
// 2 strong references. Bar destructor checks that there are no early
diff --git a/libutils/SharedBufferTest.cpp b/libutils/SharedBuffer_test.cpp
similarity index 100%
rename from libutils/SharedBufferTest.cpp
rename to libutils/SharedBuffer_test.cpp
diff --git a/libutils/tests/Singleton_test.cpp b/libutils/Singleton_test.cpp
similarity index 88%
rename from libutils/tests/Singleton_test.cpp
rename to libutils/Singleton_test.cpp
index 9acd3c3..61084b0 100644
--- a/libutils/tests/Singleton_test.cpp
+++ b/libutils/Singleton_test.cpp
@@ -30,15 +30,15 @@
TEST(SingletonTest, bug35674422) {
std::string path = android::base::GetExecutableDirectory();
- // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
+ // libutils_test_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
// definition of SingletonTestData, load it first.
- std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str());
+ std::string lib = android::base::StringPrintf("%s/libutils_test_singleton1.so", path.c_str());
void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
ASSERT_TRUE(handle1 != nullptr) << dlerror();
- // libutils_tests_singleton2.so references SingletonTestData but should not
+ // libutils_test_singleton2.so references SingletonTestData but should not
// have a definition
- lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str());
+ lib = android::base::StringPrintf("%s/libutils_test_singleton2.so", path.c_str());
void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
ASSERT_TRUE(handle2 != nullptr) << dlerror();
diff --git a/libutils/tests/Singleton_test.h b/libutils/Singleton_test.h
similarity index 100%
rename from libutils/tests/Singleton_test.h
rename to libutils/Singleton_test.h
diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/Singleton_test1.cpp
similarity index 100%
rename from libutils/tests/Singleton_test1.cpp
rename to libutils/Singleton_test1.cpp
diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/Singleton_test2.cpp
similarity index 100%
rename from libutils/tests/Singleton_test2.cpp
rename to libutils/Singleton_test2.cpp
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 0025c56..d13548e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -468,21 +468,6 @@
unlockBuffer(len);
}
-size_t String8::getUtf32Length() const
-{
- return utf8_to_utf32_length(mString, length());
-}
-
-int32_t String8::getUtf32At(size_t index, size_t *next_index) const
-{
- return utf32_from_utf8_at(mString, length(), index, next_index);
-}
-
-void String8::getUtf32(char32_t* dst) const
-{
- utf8_to_utf32(mString, length(), dst);
-}
-
// ---------------------------------------------------------------------------
// Path functions
diff --git a/libutils/tests/String8_test.cpp b/libutils/String8_test.cpp
similarity index 100%
rename from libutils/tests/String8_test.cpp
rename to libutils/String8_test.cpp
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp
similarity index 100%
rename from libutils/tests/StrongPointer_test.cpp
rename to libutils/StrongPointer_test.cpp
diff --git a/libutils/tests/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
similarity index 100%
rename from libutils/tests/SystemClock_test.cpp
rename to libutils/SystemClock_test.cpp
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 64bc402..31ca138 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -36,7 +36,7 @@
#include <utils/Log.h>
-#include <cutils/sched_policy.h>
+#include <processgroup/sched_policy.h>
#if defined(__ANDROID__)
# define __android_unused
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 5f0a51f..24a745a 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -275,25 +275,6 @@
return ss-s;
}
-
-char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
-{
- char16_t *q = dst;
- const char16_t *p = src;
- char ch;
-
- while (n) {
- n--;
- *q++ = ch = *p++;
- if ( !ch )
- break;
- }
-
- *q = 0;
-
- return dst;
-}
-
size_t strnlen16(const char16_t *s, size_t maxlen)
{
const char16_t *ss = s;
diff --git a/libutils/tests/Unicode_test.cpp b/libutils/Unicode_test.cpp
similarity index 100%
rename from libutils/tests/Unicode_test.cpp
rename to libutils/Unicode_test.cpp
diff --git a/libutils/tests/Vector_test.cpp b/libutils/Vector_test.cpp
similarity index 100%
rename from libutils/tests/Vector_test.cpp
rename to libutils/Vector_test.cpp
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 56004fe..7a4a345 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -123,13 +123,14 @@
if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
logStackInternal(logtag, stack, priority);
} else {
- ALOGW("CallStack::logStackInternal not linked");
+ ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
}
#else
- static void ALWAYS_INLINE logStack(const char*, CallStack* = getCurrent().get(),
+ static void ALWAYS_INLINE logStack(const char* logtag, CallStack* = getCurrent().get(),
android_LogPriority = ANDROID_LOG_DEBUG) {
+ ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
#endif // !WEAKS_AVAILABLE
@@ -139,13 +140,13 @@
if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
return stackToStringInternal(prefix, stack);
} else {
- return String8("<CallStack package not linked>");
+ return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
}
#else // !WEAKS_AVAILABLE
- static String8 ALWAYS_INLINE stackToString(const char* = nullptr,
+ static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
const CallStack* = getCurrent().get()) {
- return String8("<CallStack package not linked>");
+ return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
#endif // !WEAKS_AVAILABLE
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index e488e60..b04e5c1 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -47,8 +47,6 @@
return mCount.load(std::memory_order_relaxed);
}
- typedef LightRefBase<T> basetype;
-
protected:
inline ~LightRefBase() { }
diff --git a/libutils/include/utils/Mutex.h b/libutils/include/utils/Mutex.h
index 29c2e8c..1325bf3 100644
--- a/libutils/include/utils/Mutex.h
+++ b/libutils/include/utils/Mutex.h
@@ -108,7 +108,7 @@
void unlock() RELEASE();
// lock if possible; returns 0 on success, error otherwise
- status_t tryLock() TRY_ACQUIRE(true);
+ status_t tryLock() TRY_ACQUIRE(0);
#if defined(__ANDROID__)
// Lock the mutex, but don't wait longer than timeoutNs (relative time).
@@ -122,7 +122,7 @@
// which is subject to NTP adjustments, and includes time during suspend,
// so a timeout may occur even though no processes could run.
// Not holding a partial wakelock may lead to a system suspend.
- status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(true);
+ status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(0);
#endif
// Manages the mutex automatically. It'll be locked when Autolock is
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 1780cf2..42c6efb 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -171,6 +171,8 @@
#define ANDROID_REF_BASE_H
#include <atomic>
+#include <functional>
+#include <type_traits> // for common_type.
#include <stdint.h>
#include <sys/types.h>
@@ -186,25 +188,29 @@
// ---------------------------------------------------------------------------
namespace android {
-class TextOutput;
-TextOutput& printWeakPointer(TextOutput& to, const void* val);
-
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template<typename U> \
-inline bool operator _op_ (const sp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
template<typename U> \
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
+} \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
+}
+
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _wp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _wp_compare_<_compare_>(m_ptr, o); \
}
// ---------------------------------------------------------------------------
@@ -290,8 +296,6 @@
getWeakRefs()->trackMe(enable, retain);
}
- typedef RefBase basetype;
-
protected:
RefBase();
virtual ~RefBase();
@@ -354,7 +358,7 @@
public:
typedef typename RefBase::weakref_type weakref_type;
- inline wp() : m_ptr(nullptr) { }
+ inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
wp(T* other); // NOLINT(implicit)
wp(const wp<T>& other);
@@ -395,39 +399,51 @@
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
- COMPARE_WEAK(>)
- COMPARE_WEAK(<)
- COMPARE_WEAK(<=)
- COMPARE_WEAK(>=)
+ COMPARE_WEAK_FUNCTIONAL(>, std::greater)
+ COMPARE_WEAK_FUNCTIONAL(<, std::less)
+ COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)
- inline bool operator == (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
- }
template<typename U>
inline bool operator == (const wp<U>& o) const {
- return m_ptr == o.m_ptr;
+ return m_refs == o.m_refs; // Implies m_ptr == o.mptr; see invariants below.
}
- inline bool operator > (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ template<typename U>
+ inline bool operator == (const sp<U>& o) const {
+ // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older
+ // object at the same address.
+ if (o == nullptr) {
+ return m_ptr == nullptr;
+ } else {
+ return m_refs == o->getWeakRefs(); // Implies m_ptr == o.mptr.
+ }
}
+
+ template<typename U>
+ inline bool operator != (const sp<U>& o) const {
+ return !(*this == o);
+ }
+
template<typename U>
inline bool operator > (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::greater>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::greater>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator < (const wp<T>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
- }
template<typename U>
inline bool operator < (const wp<U>& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
+ if (m_ptr == o.m_ptr) {
+ return _wp_compare_<std::less>(m_refs, o.m_refs);
+ } else {
+ return _wp_compare_<std::less>(m_ptr, o.m_ptr);
+ }
}
- inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
- inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
- inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
@@ -438,19 +454,32 @@
weakref_type* m_refs;
};
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-
#undef COMPARE_WEAK
// ---------------------------------------------------------------------------
// No user serviceable parts below here.
+// Implementation invariants:
+// Either
+// 1) m_ptr and m_refs are both null, or
+// 2) m_refs == m_ptr->mRefs, or
+// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded
+// to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.
+//
+// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase
+// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both
+// null or point to the same object. If two wp's have identical m_ptr fields, they either both
+// point to the same live object and thus have the same m_ref fields, or at least one of the
+// objects is no longer live.
+//
+// Note that the above comparison operations go out of their way to provide an ordering consistent
+// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.
+
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
template<typename T>
@@ -464,16 +493,14 @@
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
wp<T>::wp(U* other)
: m_ptr(other)
{
- if (other) m_refs = other->createWeak(this);
+ m_refs = other ? other->createWeak(this) : nullptr;
}
template<typename T> template<typename U>
@@ -483,6 +510,8 @@
if (m_ptr) {
m_refs = other.m_refs;
m_refs->incWeak(this);
+ } else {
+ m_refs = nullptr;
}
}
@@ -490,9 +519,7 @@
wp<T>::wp(const sp<U>& other)
: m_ptr(other.m_ptr)
{
- if (m_ptr) {
- m_refs = m_ptr->createWeak(this);
- }
+ m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T>
@@ -595,16 +622,11 @@
{
if (m_ptr) {
m_refs->decWeak(this);
+ m_refs = 0;
m_ptr = 0;
}
}
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
-{
- return printWeakPointer(to, val.unsafe_get());
-}
-
// ---------------------------------------------------------------------------
// this class just serves as a namespace so TYPE::moveReferences can stay
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index c8f584e..0ddcbb2 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -95,13 +95,6 @@
__attribute__((format (printf, 2, 3)));
status_t appendFormatV(const char* fmt, va_list args);
- // Note that this function takes O(N) time to calculate the value.
- // No cache value is stored.
- size_t getUtf32Length() const;
- int32_t getUtf32At(size_t index,
- size_t *next_index) const;
- void getUtf32(char32_t* dst) const;
-
inline String8& operator=(const String8& other);
inline String8& operator=(const char* other);
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 1571129..9cd7c75 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_STRONG_POINTER_H
#define ANDROID_STRONG_POINTER_H
+#include <functional>
+#include <type_traits> // for common_type.
+
// ---------------------------------------------------------------------------
namespace android {
@@ -24,13 +27,12 @@
// ---------------------------------------------------------------------------
-#define COMPARE(_op_) \
-inline bool operator _op_ (const sp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
+// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<>
+// was created before the sp<>, and they point to different objects, they may
+// compare equal even if they are entirely unrelated. E.g. CameraService
+// currently performa such comparisons.
+
+#define COMPARE_STRONG(_op_) \
template<typename U> \
inline bool operator _op_ (const sp<U>& o) const { \
return m_ptr _op_ o.m_ptr; \
@@ -39,14 +41,27 @@
inline bool operator _op_ (const U* o) const { \
return m_ptr _op_ o; \
} \
-inline bool operator _op_ (const wp<T>& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template<typename U> \
-inline bool operator _op_ (const wp<U>& o) const { \
- return m_ptr _op_ o.m_ptr; \
+/* Needed to handle type inference for nullptr: */ \
+inline bool operator _op_ (const T* o) const { \
+ return m_ptr _op_ o; \
}
+template<template<typename C> class comparator, typename T, typename U>
+static inline bool _sp_compare_(T* a, U* b) {
+ return comparator<typename std::common_type<T*, U*>::type>()(a, b);
+}
+
+// Use std::less and friends to avoid undefined behavior when ordering pointers
+// to different objects.
+#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \
+template<typename U> \
+inline bool operator _op_ (const sp<U>& o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o.m_ptr); \
+} \
+template<typename U> \
+inline bool operator _op_ (const U* o) const { \
+ return _sp_compare_<_compare_>(m_ptr, o); \
+}
// ---------------------------------------------------------------------------
template<typename T>
@@ -89,12 +104,23 @@
// Operators
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
+ COMPARE_STRONG(==)
+ COMPARE_STRONG(!=)
+ COMPARE_STRONG_FUNCTIONAL(>, std::greater)
+ COMPARE_STRONG_FUNCTIONAL(<, std::less)
+ COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)
+ COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)
+
+ // Punt these to the wp<> implementation.
+ template<typename U>
+ inline bool operator == (const wp<U>& o) const {
+ return o == *this;
+ }
+
+ template<typename U>
+ inline bool operator != (const wp<U>& o) const {
+ return o != *this;
+ }
private:
template<typename Y> friend class sp;
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index 3525138..fc67656 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -47,6 +47,7 @@
virtual ~Thread();
// Start the thread in threadLoop() which needs to be implemented.
+ // NOLINTNEXTLINE(google-default-arguments)
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index 61a1b4f..a2aaa47 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -28,7 +28,6 @@
size_t strlen16(const char16_t *);
size_t strnlen16(const char16_t *, size_t);
char16_t *strcpy16(char16_t *, const char16_t *);
-char16_t *strncpy16(char16_t *, const char16_t *, size_t);
char16_t *strstr16(const char16_t*, const char16_t*);
// Version of comparison that supports embedded NULs.
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
deleted file mode 100644
index 1390552..0000000
--- a/libutils/tests/Android.bp
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-// Build the unit tests.
-
-cc_test {
- name: "libutils_tests",
- host_supported: true,
-
- srcs: [
- "BitSet_test.cpp",
- "LruCache_test.cpp",
- "Mutex_test.cpp",
- "Singleton_test.cpp",
- "String8_test.cpp",
- "StrongPointer_test.cpp",
- "Unicode_test.cpp",
- "Vector_test.cpp",
- ],
-
- target: {
- android: {
- srcs: [
- "SystemClock_test.cpp",
- ],
- shared_libs: [
- "libz",
- "liblog",
- "libcutils",
- "libutils",
- "libbase",
- ],
- },
- linux: {
- srcs: [
- "Looper_test.cpp",
- "RefBase_test.cpp",
- ],
- },
- host: {
- static_libs: [
- "libutils",
- "liblog",
- "libbase",
- ],
- },
- },
-
- required: [
- "libutils_tests_singleton1",
- "libutils_tests_singleton2",
- ],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wthread-safety",
- ],
-}
-
-cc_test_library {
- name: "libutils_tests_singleton1",
- host_supported: true,
- relative_install_path: "libutils_tests",
- srcs: ["Singleton_test1.cpp"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_test_library {
- name: "libutils_tests_singleton2",
- host_supported: true,
- relative_install_path: "libutils_tests",
- srcs: ["Singleton_test2.cpp"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- shared_libs: ["libutils_tests_singleton1"],
-}
diff --git a/libutils/tests/README.txt b/libutils/tests/README.txt
deleted file mode 100644
index ad54e57..0000000
--- a/libutils/tests/README.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-Run device tests:
-
-mma -j<whatever>
-(after adb root; adb disable-verity; adb reboot)
-adb root
-adb remount
-adb sync
-adb shell /data/nativetest/libutils_tests/libutils_tests
diff --git a/libutils/tests/TestHelpers.h b/libutils/tests/TestHelpers.h
deleted file mode 100644
index 6801cd7..0000000
--- a/libutils/tests/TestHelpers.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TESTHELPERS_H
-#define TESTHELPERS_H
-
-#include <utils/threads.h>
-
-namespace android {
-
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
-class DelayedTask : public Thread {
- int mDelayMillis;
-
-public:
- explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
-
-protected:
- virtual ~DelayedTask() { }
-
- virtual void doTask() = 0;
-
- virtual bool threadLoop() {
- usleep(mDelayMillis * 1000);
- doTask();
- return false;
- }
-};
-
-} // namespace android
-
-#endif // TESTHELPERS_H
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e73b366..f4544a1 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -2,18 +2,28 @@
cc_library {
name: "libvndksupport",
- srcs: ["linker.c"],
+ native_bridge_supported: true,
+ srcs: ["linker.cpp"],
cflags: [
"-Wall",
"-Werror",
],
local_include_dirs: ["include/vndksupport"],
export_include_dirs: ["include"],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "libdl_android",
+ "liblog",
+ ],
+ version_script: "libvndksupport.map.txt",
+ stubs: {
+ symbol_file: "libvndksupport.map.txt",
+ versions: ["29"],
+ },
}
llndk_library {
name: "libvndksupport",
+ native_bridge_supported: true,
symbol_file: "libvndksupport.map.txt",
export_include_dirs: ["include"],
}
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index f509564..5f48c39 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,6 +20,16 @@
extern "C" {
#endif
+/*
+ * Returns whether the current process is a vendor process.
+ *
+ * Note that this is only checking what process is running and has nothing to
+ * do with what namespace the caller is loaded at. For example, a VNDK-SP
+ * library loaded by SP-HAL calling this function may still get a 'false',
+ * because it is running in a system process.
+ */
+int android_is_in_vendor_process();
+
void* android_load_sphal_library(const char* name, int flag);
int android_unload_sphal_library(void* handle);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index 16e38da..ac9a99c 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,7 +1,8 @@
LIBVNDKSUPPORT {
global:
- android_load_sphal_library; # vndk
- android_unload_sphal_library; # vndk
+ android_is_in_vendor_process; # vndk apex
+ android_load_sphal_library; # vndk apex
+ android_unload_sphal_library; # vndk apex
local:
*;
};
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
deleted file mode 100644
index bc5620b..0000000
--- a/libvndksupport/linker.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "linker.h"
-
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#define LOG_TAG "vndksupport"
-#include <log/log.h>
-
-__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
-
-static const char* namespace_name = NULL;
-
-static struct android_namespace_t* get_vendor_namespace() {
- const char* namespace_names[] = {"sphal", "default", NULL};
- static struct android_namespace_t* vendor_namespace = NULL;
- if (vendor_namespace == NULL) {
- int name_idx = 0;
- while (namespace_names[name_idx] != NULL) {
- if (android_get_exported_namespace != NULL) {
- vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
- }
- if (vendor_namespace != NULL) {
- namespace_name = namespace_names[name_idx];
- break;
- }
- name_idx++;
- }
- }
- return vendor_namespace;
-}
-
-void* android_load_sphal_library(const char* name, int flag) {
- struct android_namespace_t* vendor_namespace = get_vendor_namespace();
- if (vendor_namespace != NULL) {
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
- };
- void* handle = NULL;
- if (android_dlopen_ext != NULL) {
- handle = android_dlopen_ext(name, flag, &dlextinfo);
- }
- if (!handle) {
- ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
- }
- return handle;
- } else {
- ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
- return dlopen(name, flag);
- }
-}
-
-int android_unload_sphal_library(void* handle) {
- return dlclose(handle);
-}
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
new file mode 100644
index 0000000..cf0f618
--- /dev/null
+++ b/libvndksupport/linker.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "vndksupport"
+
+#include "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <initializer_list>
+
+__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
+ const android_dlextinfo*);
+
+namespace {
+
+struct VendorNamespace {
+ android_namespace_t* ptr = nullptr;
+ const char* name = nullptr;
+};
+
+} // anonymous namespace
+
+static VendorNamespace get_vendor_namespace() {
+ static VendorNamespace result = ([] {
+ for (const char* name : {"sphal", "default"}) {
+ if (android_get_exported_namespace != nullptr) {
+ if (android_namespace_t* ns = android_get_exported_namespace(name)) {
+ return VendorNamespace{ns, name};
+ }
+ }
+ }
+ return VendorNamespace{};
+ })();
+ return result;
+}
+
+int android_is_in_vendor_process() {
+ // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+ // loaded (sysprop service isn't up for init to know <ver>).
+ if (getpid() == 1) {
+ return 0;
+ }
+ if (android_get_exported_namespace == nullptr) {
+ ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+ return 0;
+ }
+
+ // In vendor process, 'vndk' namespace is not visible, whereas in system
+ // process, it is.
+ return android_get_exported_namespace("vndk") == nullptr;
+}
+
+void* android_load_sphal_library(const char* name, int flag) {
+ VendorNamespace vendor_namespace = get_vendor_namespace();
+ if (vendor_namespace.ptr != nullptr) {
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = vendor_namespace.ptr,
+ };
+ void* handle = nullptr;
+ if (android_dlopen_ext != nullptr) {
+ handle = android_dlopen_ext(name, flag, &dlextinfo);
+ }
+ if (!handle) {
+ ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
+ dlerror());
+ }
+ return handle;
+ } else {
+ ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+ return dlopen(name, flag);
+ }
+}
+
+int android_unload_sphal_library(void* handle) {
+ return dlclose(handle);
+}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
index 7ce27d4..d0c8ef7 100644
--- a/libvndksupport/tests/linker_test.cpp
+++ b/libvndksupport/tests/linker_test.cpp
@@ -21,11 +21,6 @@
#include <vndksupport/linker.h>
#include <string>
-// Since the test executable will be in /data and ld.config.txt does not
-// configure sphal namespace for executables in /data, the call to
-// android_load_sphal_library will always fallback to the plain dlopen from the
-// default namespace.
-
// Let's use libEGL_<chipset>.so as a SP-HAL in test
static std::string find_sphal_lib() {
const char* path =
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2095189..0253f2f 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -26,6 +26,8 @@
// Incorrectly warns when C++11 empty brace {} initializer is used.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
"-Wno-missing-field-initializers",
+ "-Wconversion",
+ "-Wno-sign-conversion",
],
// Enable -Wold-style-cast only for non-Windows targets. _islower_l,
@@ -38,6 +40,19 @@
],
},
},
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ "shift",
+ "integer-divide-by-zero",
+ "implicit-signed-integer-truncation",
+ // TODO: Fix crash when we enable this option
+ // "implicit-unsigned-integer-truncation",
+ // TODO: not tested yet.
+ // "implicit-integer-sign-change",
+ ],
+ },
}
cc_defaults {
@@ -61,6 +76,10 @@
"liblog",
],
+ // for FRIEND_TEST
+ static_libs: ["libgtest_prod"],
+ export_static_lib_headers: ["libgtest_prod"],
+
export_include_dirs: ["include"],
}
@@ -69,6 +88,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
vndk: {
enabled: true,
},
@@ -125,6 +145,7 @@
enabled: true,
},
},
+ test_suites: ["device-tests"],
}
// Performance benchmarks.
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index 5fc2fb4..10311b5 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -20,9 +20,15 @@
#include <stddef.h>
#include <stdint.h>
+#include <limits>
+
// Check if |length| bytes at |entry_name| constitute a valid entry name.
-// Entry names must be valid UTF-8 and must not contain '0'.
+// Entry names must be valid UTF-8 and must not contain '0'. They also must
+// fit into the central directory record.
inline bool IsValidEntryName(const uint8_t* entry_name, const size_t length) {
+ if (length > std::numeric_limits<uint16_t>::max()) {
+ return false;
+ }
for (size_t i = 0; i < length; ++i) {
const uint8_t byte = entry_name[i];
if (byte == 0) {
@@ -35,7 +41,8 @@
return false;
} else {
// 2-5 byte sequences.
- for (uint8_t first = byte << 1; first & 0x80; first <<= 1) {
+ for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
+ first = static_cast<uint8_t>((first & 0x7f) << 1)) {
++i;
// Missing continuation byte..
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index ab38dfd..e3ac114 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,6 +25,9 @@
#include <sys/cdefs.h>
#include <sys/types.h>
+#include <string>
+#include <string_view>
+
#include "android-base/off64_t.h"
/* Zip compression methods we support */
@@ -33,32 +36,6 @@
kCompressDeflated = 8, // standard deflate
};
-struct ZipString {
- const uint8_t* name;
- uint16_t name_length;
-
- ZipString() {}
-
- /*
- * entry_name has to be an c-style string with only ASCII characters.
- */
- explicit ZipString(const char* entry_name);
-
- bool operator==(const ZipString& rhs) const {
- return name && (name_length == rhs.name_length) && (memcmp(name, rhs.name, name_length) == 0);
- }
-
- bool StartsWith(const ZipString& prefix) const {
- return name && (name_length >= prefix.name_length) &&
- (memcmp(name, prefix.name, prefix.name_length) == 0);
- }
-
- bool EndsWith(const ZipString& suffix) const {
- return name && (name_length >= suffix.name_length) &&
- (memcmp(name + name_length - suffix.name_length, suffix.name, suffix.name_length) == 0);
- }
-};
-
/*
* Represents information about a zip entry in a zip file.
*/
@@ -149,8 +126,7 @@
void CloseArchive(ZipArchiveHandle archive);
/*
- * Find an entry in the Zip archive, by name. |entryName| must be a null
- * terminated string, and |data| must point to a writeable memory location.
+ * Find an entry in the Zip archive, by name. |data| must be non-null.
*
* Returns 0 if an entry is found, and populates |data| with information
* about this entry. Returns negative values otherwise.
@@ -164,7 +140,7 @@
* On non-Windows platforms this method does not modify internal state and
* can be called concurrently.
*/
-int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
/*
* Start iterating over all entries of a zip file. The order of iteration
@@ -180,7 +156,8 @@
* Returns 0 on success and negative values on failure.
*/
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const ZipString* optional_prefix, const ZipString* optional_suffix);
+ const std::string_view optional_prefix = "",
+ const std::string_view optional_suffix = "");
/*
* Advance to the next element in the zipfile in iteration order.
@@ -188,7 +165,8 @@
* Returns 0 on success, -1 if there are no more elements in this
* archive and lower negative values on failure.
*/
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string* name);
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
/*
* End iteration over all entries of a zip file and frees the memory allocated
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index f6c8427..d68683d 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,8 +19,10 @@
#include <cstdio>
#include <ctime>
+#include <gtest/gtest_prod.h>
#include <memory>
#include <string>
+#include <string_view>
#include <vector>
#include "android-base/macros.h"
@@ -76,7 +78,7 @@
uint32_t uncompressed_size;
uint16_t last_mod_time;
uint16_t last_mod_date;
- uint32_t padding_length;
+ uint16_t padding_length;
off64_t local_file_header_offset;
};
@@ -101,7 +103,7 @@
* Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
* Returns 0 on success, and an error value < 0 on failure.
*/
- int32_t StartEntry(const char* path, size_t flags);
+ int32_t StartEntry(std::string_view path, size_t flags);
/**
* Starts a new zip entry with the given path and flags, where the
@@ -111,17 +113,17 @@
* Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
* Returns 0 on success, and an error value < 0 on failure.
*/
- int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+ int32_t StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment);
/**
* Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
*/
- int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+ int32_t StartEntryWithTime(std::string_view path, size_t flags, time_t time);
/**
* Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
*/
- int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
+ int32_t StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time, uint32_t alignment);
/**
* Writes bytes to the zip file for the previously started zip entry.
@@ -161,9 +163,10 @@
int32_t HandleError(int32_t error_code);
int32_t PrepareDeflate();
- int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
- int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
+ int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
+ int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
int32_t FlushCompressedBytes(FileEntry* file);
+ bool ShouldUseDataDescriptor() const;
enum class State {
kWritingZip,
@@ -181,4 +184,6 @@
std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
std::vector<uint8_t> buffer_;
+
+ FRIEND_TEST(zipwriter, WriteToUnseekableFile);
};
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 6756007..426325e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -17,6 +17,7 @@
#include <errno.h>
#include <error.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
@@ -52,9 +53,21 @@
static uint64_t total_compressed_length = 0;
static size_t file_count = 0;
-static bool Filter(const std::string& name) {
- if (!excludes.empty() && excludes.find(name) != excludes.end()) return true;
- if (!includes.empty() && includes.find(name) == includes.end()) return true;
+static bool ShouldInclude(const std::string& name) {
+ // Explicitly excluded?
+ if (!excludes.empty()) {
+ for (const auto& exclude : excludes) {
+ if (!fnmatch(exclude.c_str(), name.c_str(), 0)) return false;
+ }
+ }
+
+ // Implicitly included?
+ if (includes.empty()) return true;
+
+ // Explicitly included?
+ for (const auto& include : includes) {
+ if (!fnmatch(include.c_str(), name.c_str(), 0)) return true;
+ }
return false;
}
@@ -72,7 +85,7 @@
static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
if (uncompressed == 0) return 0;
- return (100LL * (uncompressed - compressed)) / uncompressed;
+ return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
}
static void MaybeShowHeader() {
@@ -236,16 +249,15 @@
// libziparchive iteration order doesn't match the central directory.
// We could sort, but that would cost extra and wouldn't match either.
void* cookie;
- int err = StartIteration(zah, &cookie, nullptr, nullptr);
+ int err = StartIteration(zah, &cookie);
if (err != 0) {
error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
}
ZipEntry entry;
- ZipString string;
- while ((err = Next(cookie, &entry, &string)) >= 0) {
- std::string name(string.name, string.name + string.name_length);
- if (!Filter(name)) ProcessOne(zah, entry, name);
+ std::string name;
+ while ((err = Next(cookie, &entry, &name)) >= 0) {
+ if (ShouldInclude(name)) ProcessOne(zah, entry, name);
}
if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
@@ -260,7 +272,8 @@
printf(
"\n"
- "Extract FILEs from ZIP archive. Default is all files.\n"
+ "Extract FILEs from ZIP archive. Default is all files. Both the include and\n"
+ "exclude (-x) lists use shell glob patterns.\n"
"\n"
"-d DIR Extract into DIR\n"
"-l List contents (-lq excludes archive name, -lv is verbose)\n"
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9eb7f2c..c95b035 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -47,6 +47,7 @@
#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
#include <android-base/mapped_file.h>
#include <android-base/memory.h>
+#include <android-base/strings.h>
#include <android-base/utf8.h>
#include <log/log.h>
#include "zlib.h"
@@ -101,38 +102,8 @@
return val;
}
-static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
- return std::hash<std::string_view>{}(
- std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
- // Remove this code path once the windows compiler knows how to compile the above statement.
- uint32_t hash = 0;
- uint16_t len = name.name_length;
- const uint8_t* str = name.name;
-
- while (len--) {
- hash = hash * 31 + *str++;
- }
-
- return hash;
-#endif
-}
-
-static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
- const ZipStringOffset& zip_string_offset) {
- const ZipString from_offset = zip_string_offset.GetZipString(start);
- return from_offset == zip_string;
-}
-
-/**
- * Returns offset of ZipString#name from the start of the central directory in the memory map.
- * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
- */
-static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
- CHECK_GT(name, start);
- CHECK_LT(name, start + 0xffffff);
- return static_cast<uint32_t>(name - start);
+static uint32_t ComputeHash(std::string_view name) {
+ return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
}
/*
@@ -140,27 +111,27 @@
* valid range.
*/
static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
- const ZipString& name, const uint8_t* start) {
+ std::string_view name, const uint8_t* start) {
const uint32_t hash = ComputeHash(name);
// NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
uint32_t ent = hash & (hash_table_size - 1);
while (hash_table[ent].name_offset != 0) {
- if (isZipStringEqual(start, name, hash_table[ent])) {
+ if (hash_table[ent].ToStringView(start) == name) {
return ent;
}
ent = (ent + 1) & (hash_table_size - 1);
}
- ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
+ ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
return kEntryNotFound;
}
/*
* Add a new entry to the hash table.
*/
-static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
- const ZipString& name, const uint8_t* start) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
+ std::string_view name, const uint8_t* start) {
const uint64_t hash = ComputeHash(name);
uint32_t ent = hash & (hash_table_size - 1);
@@ -169,15 +140,18 @@
* Further, we guarantee that the hashtable size is not 0.
*/
while (hash_table[ent].name_offset != 0) {
- if (isZipStringEqual(start, name, hash_table[ent])) {
- // We've found a duplicate entry. We don't accept it
- ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
+ if (hash_table[ent].ToStringView(start) == name) {
+ // We've found a duplicate entry. We don't accept duplicates.
+ ALOGW("Zip: Found duplicate entry %.*s", static_cast<int>(name.size()), name.data());
return kDuplicateEntry;
}
ent = (ent + 1) & (hash_table_size - 1);
}
- hash_table[ent].name_offset = GetOffset(name.name, start);
- hash_table[ent].name_length = name.name_length;
+
+ // `name` has already been validated before entry.
+ const char* start_char = reinterpret_cast<const char*>(start);
+ hash_table[ent].name_offset = static_cast<uint32_t>(name.data() - start_char);
+ hash_table[ent].name_length = static_cast<uint16_t>(name.size());
return 0;
}
@@ -227,7 +201,7 @@
}
static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
- off64_t file_length, off64_t read_amount,
+ off64_t file_length, uint32_t read_amount,
uint8_t* scan_buffer) {
const off64_t search_start = file_length - read_amount;
@@ -243,7 +217,8 @@
* doing an initial minimal read; if we don't find it, retry with a
* second read as above.)
*/
- int i = read_amount - sizeof(EocdRecord);
+ CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
+ int32_t i = read_amount - sizeof(EocdRecord);
for (; i >= 0; i--) {
if (scan_buffer[i] == 0x50) {
uint32_t* sig_addr = reinterpret_cast<uint32_t*>(&scan_buffer[i]);
@@ -278,11 +253,6 @@
if (static_cast<off64_t>(eocd->cd_start_offset) + eocd->cd_size > eocd_offset) {
ALOGW("Zip: bad offsets (dir %" PRIu32 ", size %" PRIu32 ", eocd %" PRId64 ")",
eocd->cd_start_offset, eocd->cd_size, static_cast<int64_t>(eocd_offset));
-#if defined(__ANDROID__)
- if (eocd->cd_start_offset + eocd->cd_size <= eocd_offset) {
- android_errorWriteLog(0x534e4554, "31251826");
- }
-#endif
return kInvalidOffset;
}
if (eocd->num_records == 0) {
@@ -351,9 +321,9 @@
*
* We start by pulling in the last part of the file.
*/
- off64_t read_amount = kMaxEOCDSearch;
+ uint32_t read_amount = kMaxEOCDSearch;
if (file_length < read_amount) {
- read_amount = file_length;
+ read_amount = static_cast<uint32_t>(file_length);
}
std::vector<uint8_t> scan_buffer(read_amount);
@@ -383,7 +353,7 @@
reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
if (archive->hash_table == nullptr) {
ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
- archive->hash_table_size, sizeof(ZipString));
+ archive->hash_table_size, sizeof(ZipStringOffset));
return -1;
}
@@ -421,21 +391,19 @@
const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
if (file_name + file_name_length > cd_end) {
- ALOGW(
- "Zip: file name boundary exceeds the central directory range, file_name_length: "
- "%" PRIx16 ", cd_length: %zu",
- file_name_length, cd_length);
+ ALOGW("Zip: file name for entry %" PRIu16
+ " exceeds the central directory range, file_name_length: %" PRIu16 ", cd_length: %zu",
+ i, file_name_length, cd_length);
return -1;
}
- /* check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters */
+ // Check that file name is valid UTF-8 and doesn't contain NUL (U+0000) characters.
if (!IsValidEntryName(file_name, file_name_length)) {
+ ALOGW("Zip: invalid file name at entry %" PRIu16, i);
return -1;
}
- /* add the CDE filename to the hash table */
- ZipString entry_name;
- entry_name.name = file_name;
- entry_name.name_length = file_name_length;
+ // Add the CDE filename to the hash table.
+ std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
archive->central_directory.GetBasePtr());
if (add_result != 0) {
@@ -491,7 +459,7 @@
}
int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
- const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY, 0);
+ const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
ZipArchive* archive = new ZipArchive(fd, true);
*handle = archive;
@@ -550,21 +518,19 @@
return 0;
}
-static int32_t FindEntry(const ZipArchive* archive, const int ent, ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
const uint16_t nameLen = archive->hash_table[ent].name_length;
// Recover the start of the central directory entry from the filename
// pointer. The filename is the first entry past the fixed-size data,
// so we can just subtract back from that.
- const ZipString from_offset =
- archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
- const uint8_t* ptr = from_offset.name;
+ const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
+ const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
ptr -= sizeof(CentralDirectoryRecord);
// This is the base of our mmapped region, we have to sanity check that
// the name that's in the hash table is a pointer to a location within
// this mapped region.
- const uint8_t* base_ptr = archive->central_directory.GetBasePtr();
if (ptr < base_ptr || ptr > base_ptr + archive->central_directory.GetMapLength()) {
ALOGW("Zip: Invalid entry pointer");
return kInvalidOffset;
@@ -656,26 +622,24 @@
// Check that the local file header name matches the declared
// name in the central directory.
- if (lfh->file_name_length == nameLen) {
- const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
- if (name_offset + lfh->file_name_length > cd_offset) {
- ALOGW("Zip: Invalid declared length");
- return kInvalidOffset;
- }
-
- std::vector<uint8_t> name_buf(nameLen);
- if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
- ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
- return kIoError;
- }
- const ZipString from_offset =
- archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
- if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
- return kInconsistentInformation;
- }
-
- } else {
- ALOGW("Zip: lfh name did not match central directory.");
+ if (lfh->file_name_length != nameLen) {
+ ALOGW("Zip: lfh name length did not match central directory");
+ return kInconsistentInformation;
+ }
+ const off64_t name_offset = local_header_offset + sizeof(LocalFileHeader);
+ if (name_offset + lfh->file_name_length > cd_offset) {
+ ALOGW("Zip: lfh name has invalid declared length");
+ return kInvalidOffset;
+ }
+ std::vector<uint8_t> name_buf(nameLen);
+ if (!archive->mapped_zip.ReadAtOffset(name_buf.data(), nameLen, name_offset)) {
+ ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
+ return kIoError;
+ }
+ const std::string_view entry_name =
+ archive->hash_table[ent].ToStringView(archive->central_directory.GetBasePtr());
+ if (memcmp(entry_name.data(), name_buf.data(), nameLen) != 0) {
+ ALOGW("Zip: lfh name did not match central directory");
return kInconsistentInformation;
}
@@ -706,52 +670,32 @@
}
struct IterationHandle {
- uint32_t position;
- // We're not using vector here because this code is used in the Windows SDK
- // where the STL is not available.
- ZipString prefix;
- ZipString suffix;
ZipArchive* archive;
- IterationHandle(const ZipString* in_prefix, const ZipString* in_suffix) {
- if (in_prefix) {
- uint8_t* name_copy = new uint8_t[in_prefix->name_length];
- memcpy(name_copy, in_prefix->name, in_prefix->name_length);
- prefix.name = name_copy;
- prefix.name_length = in_prefix->name_length;
- } else {
- prefix.name = NULL;
- prefix.name_length = 0;
- }
- if (in_suffix) {
- uint8_t* name_copy = new uint8_t[in_suffix->name_length];
- memcpy(name_copy, in_suffix->name, in_suffix->name_length);
- suffix.name = name_copy;
- suffix.name_length = in_suffix->name_length;
- } else {
- suffix.name = NULL;
- suffix.name_length = 0;
- }
- }
+ std::string prefix;
+ std::string suffix;
- ~IterationHandle() {
- delete[] prefix.name;
- delete[] suffix.name;
- }
+ uint32_t position = 0;
+
+ IterationHandle(ZipArchive* archive, std::string_view in_prefix, std::string_view in_suffix)
+ : archive(archive), prefix(in_prefix), suffix(in_suffix) {}
};
int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
- const ZipString* optional_prefix, const ZipString* optional_suffix) {
+ const std::string_view optional_prefix,
+ const std::string_view optional_suffix) {
if (archive == NULL || archive->hash_table == NULL) {
ALOGW("Zip: Invalid ZipArchiveHandle");
return kInvalidHandle;
}
- IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
- cookie->position = 0;
- cookie->archive = archive;
+ if (optional_prefix.size() > static_cast<size_t>(UINT16_MAX) ||
+ optional_suffix.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: prefix/suffix too long");
+ return kInvalidEntryName;
+ }
- *cookie_ptr = cookie;
+ *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
return 0;
}
@@ -759,22 +703,33 @@
delete reinterpret_cast<IterationHandle*>(cookie);
}
-int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
- if (entryName.name_length == 0) {
- ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
+int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName,
+ ZipEntry* data) {
+ if (entryName.empty() || entryName.size() > static_cast<size_t>(UINT16_MAX)) {
+ ALOGW("Zip: Invalid filename of length %zu", entryName.size());
return kInvalidEntryName;
}
const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
archive->central_directory.GetBasePtr());
if (ent < 0) {
- ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
- return ent;
+ ALOGV("Zip: Could not find entry %.*s", static_cast<int>(entryName.size()), entryName.data());
+ return static_cast<int32_t>(ent); // kEntryNotFound is safe to truncate.
}
- return FindEntry(archive, ent, data);
+ // We know there are at most hash_table_size entries, safe to truncate.
+ return FindEntry(archive, static_cast<uint32_t>(ent), data);
}
-int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
+int32_t Next(void* cookie, ZipEntry* data, std::string* name) {
+ std::string_view sv;
+ int32_t result = Next(cookie, data, &sv);
+ if (result == 0 && name) {
+ *name = std::string(sv);
+ }
+ return result;
+}
+
+int32_t Next(void* cookie, ZipEntry* data, std::string_view* name) {
IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
if (handle == NULL) {
ALOGW("Zip: Null ZipArchiveHandle");
@@ -791,16 +746,14 @@
const uint32_t hash_table_length = archive->hash_table_size;
const ZipStringOffset* hash_table = archive->hash_table;
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
- const ZipString from_offset =
- hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
- if (hash_table[i].name_offset != 0 &&
- (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
- (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
+ const std::string_view entry_name =
+ hash_table[i].ToStringView(archive->central_directory.GetBasePtr());
+ if (hash_table[i].name_offset != 0 && (android::base::StartsWith(entry_name, handle->prefix) &&
+ android::base::EndsWith(entry_name, handle->suffix))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
- if (!error) {
- name->name = from_offset.name;
- name->name_length = hash_table[i].name_length;
+ if (!error && name) {
+ *name = entry_name;
}
return error;
}
@@ -854,7 +807,6 @@
return FileWriter{};
}
- int result = 0;
#if defined(__linux__)
if (declared_length > 0) {
// Make sure we have enough space on the volume to extract the compressed
@@ -866,7 +818,7 @@
// EOPNOTSUPP error when issued in other filesystems.
// Hence, check for the return error code before concluding that the
// disk does not have enough space.
- result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+ long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
if (result == -1 && errno == ENOSPC) {
ALOGW("Zip: unable to allocate %" PRId64 " bytes at offset %" PRId64 ": %s",
static_cast<int64_t>(declared_length), static_cast<int64_t>(current_offset),
@@ -884,7 +836,7 @@
// Block device doesn't support ftruncate(2).
if (!S_ISBLK(sb.st_mode)) {
- result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+ long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
if (result == -1) {
ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
static_cast<int64_t>(declared_length + current_offset), strerror(errno));
@@ -1003,16 +955,16 @@
std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
const bool compute_crc = (crc_out != nullptr);
- uint64_t crc = 0;
+ uLong crc = 0;
uint32_t remaining_bytes = compressed_length;
do {
/* read as much as we can */
if (zstream.avail_in == 0) {
- const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+ const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
const uint32_t offset = (compressed_length - remaining_bytes);
// Make sure to read at offset to ensure concurrent access to the fd.
if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
- ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
+ ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
return kIoError;
}
@@ -1036,7 +988,8 @@
if (!writer->Append(&write_buf[0], write_size)) {
return kIoError;
} else if (compute_crc) {
- crc = crc32(crc, &write_buf[0], write_size);
+ DCHECK_LE(write_size, kBufSize);
+ crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
}
zstream.next_out = &write_buf[0];
@@ -1081,17 +1034,17 @@
const uint32_t length = entry->uncompressed_length;
uint32_t count = 0;
- uint64_t crc = 0;
+ uLong crc = 0;
while (count < length) {
uint32_t remaining = length - count;
off64_t offset = entry->offset + count;
// Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
- const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+ const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
// Make sure to read at offset to ensure concurrent access to the fd.
if (!mapped_zip.ReadAtOffset(buf.data(), block_size, offset)) {
- ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
+ ALOGW("CopyFileToFile: copy read failed, block_size = %u, offset = %" PRId64 ": %s",
block_size, static_cast<int64_t>(offset), strerror(errno));
return kIoError;
}
@@ -1168,12 +1121,6 @@
return archive->mapped_zip.GetFileDescriptor();
}
-ZipString::ZipString(const char* entry_name) : name(reinterpret_cast<const uint8_t*>(entry_name)) {
- size_t len = strlen(entry_name);
- CHECK_LE(len, static_cast<size_t>(UINT16_MAX));
- name_length = static_cast<uint16_t>(len);
-}
-
#if !defined(_WIN32)
class ProcessWriter : public zip_archive::Writer {
public:
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 46aa5a6..09d3b8a 100644
--- a/libziparchive/zip_archive_benchmark.cpp
+++ b/libziparchive/zip_archive_benchmark.cpp
@@ -55,10 +55,10 @@
// In order to walk through all file names in the archive, look for a name
// that does not exist in the archive.
- ZipString name("thisFileNameDoesNotExist");
+ std::string_view name("thisFileNameDoesNotExist");
// Start the benchmark.
- while (state.KeepRunning()) {
+ for (auto _ : state) {
OpenArchive(temp_file->path, &handle);
FindEntry(handle, name, &data);
CloseArchive(handle);
@@ -71,11 +71,11 @@
ZipArchiveHandle handle;
void* iteration_cookie;
ZipEntry data;
- ZipString name;
+ std::string name;
- while (state.KeepRunning()) {
+ for (auto _ : state) {
OpenArchive(temp_file->path, &handle);
- StartIteration(handle, &iteration_cookie, nullptr, nullptr);
+ StartIteration(handle, &iteration_cookie);
while (Next(iteration_cookie, &data, &name) == 0) {
}
EndIteration(iteration_cookie);
@@ -84,4 +84,27 @@
}
BENCHMARK(Iterate_all_files);
+static void StartAlignedEntry(benchmark::State& state) {
+ TemporaryFile file;
+ FILE* fp = fdopen(file.fd, "w");
+
+ ZipWriter writer(fp);
+
+ auto alignment = uint32_t(state.range(0));
+ std::string name = "name";
+ int counter = 0;
+ for (auto _ : state) {
+ writer.StartAlignedEntry(name + std::to_string(counter++), 0, alignment);
+ state.PauseTiming();
+ writer.WriteBytes("hola", 4);
+ writer.FinishEntry();
+ state.ResumeTiming();
+ }
+
+ writer.Finish();
+ fclose(fp);
+}
+BENCHMARK(StartAlignedEntry)->Arg(2)->Arg(16)->Arg(1024)->Arg(4096);
+
+
BENCHMARK_MAIN();
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 330a02a..30a1d72 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -137,22 +137,22 @@
};
/**
- * More space efficient string representation of strings in an mmaped zipped file than
- * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
- * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
- * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
- * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
- * of the entire address, consuming 8 bytes with alignment.
+ * More space efficient string representation of strings in an mmaped zipped
+ * file than std::string_view. Using std::string_view as an entry in the
+ * ZipArchive hash table wastes space. std::string_view stores a pointer to a
+ * string (on 64 bit, 8 bytes) and the length to read from that pointer,
+ * 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting
+ * 6 bytes.
+ *
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory
+ * mapped file instead of the entire address, consuming 8 bytes with alignment.
*/
struct ZipStringOffset {
uint32_t name_offset;
uint16_t name_length;
- const ZipString GetZipString(const uint8_t* start) const {
- ZipString zip_string;
- zip_string.name = start + name_offset;
- zip_string.name_length = name_length;
- return zip_string;
+ const std::string_view ToStringView(const uint8_t* start) const {
+ return std::string_view{reinterpret_cast<const char*>(start + name_offset), name_length};
}
};
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 9ec89b1..1ec95b6 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -27,6 +27,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <log/log.h>
#include <ziparchive/zip_archive.h>
@@ -77,6 +78,12 @@
}
const std::vector<uint8_t>* ZipArchiveStreamEntryUncompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(data_.capacity(), kBufSize);
+
if (length_ == 0) {
return nullptr;
}
@@ -97,7 +104,8 @@
if (bytes < data_.size()) {
data_.resize(bytes);
}
- computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
length_ -= bytes;
offset_ += bytes;
return &data_;
@@ -192,9 +200,15 @@
}
const std::vector<uint8_t>* ZipArchiveStreamEntryCompressed::Read() {
+ // Simple sanity check. The vector should *only* be handled by this code. A caller
+ // should not const-cast and modify the capacity. This may invalidate next_out.
+ //
+ // Note: it would be better to store the results of data() across Read calls.
+ CHECK_EQ(out_.capacity(), kBufSize);
+
if (z_stream_.avail_out == 0) {
z_stream_.next_out = out_.data();
- z_stream_.avail_out = out_.size();
+ z_stream_.avail_out = static_cast<uint32_t>(out_.size());
;
}
@@ -203,7 +217,9 @@
if (compressed_length_ == 0) {
return nullptr;
}
- size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
+ DCHECK_LE(in_.size(), std::numeric_limits<uint32_t>::max()); // Should be buf size = 64k.
+ uint32_t bytes = (compressed_length_ > in_.size()) ? static_cast<uint32_t>(in_.size())
+ : compressed_length_;
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
errno = 0;
if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
@@ -230,14 +246,16 @@
if (z_stream_.avail_out == 0) {
uncompressed_length_ -= out_.size();
- computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
return &out_;
}
if (zerr == Z_STREAM_END) {
if (z_stream_.avail_out != 0) {
// Resize the vector down to the actual size of the data.
out_.resize(out_.size() - z_stream_.avail_out);
- computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
+ computed_crc32_ = static_cast<uint32_t>(
+ crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
uncompressed_length_ -= out_.size();
return &out_;
}
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 0ea7d5d..8781ab7 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,8 +27,8 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/mapped_file.h>
-#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
@@ -64,11 +64,6 @@
return OpenArchive(abs_path.c_str(), handle);
}
-static void SetZipString(ZipString* zip_str, const std::string& str) {
- zip_str->name = reinterpret_cast<const uint8_t*>(str.c_str());
- zip_str->name_length = str.size();
-}
-
TEST(ziparchive, Open) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -112,7 +107,27 @@
close(fd);
}
-static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
+TEST(ziparchive, Iteration_std_string_view) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+
+ ZipEntry data;
+ std::vector<std::string_view> names;
+ std::string_view name;
+ while (Next(iteration_cookie, &data, &name) == 0) names.push_back(name);
+
+ // Assert that the names are as expected.
+ std::vector<std::string_view> expected_names{"a.txt", "b.txt", "b/", "b/c.txt", "b/d.txt"};
+ std::sort(names.begin(), names.end());
+ ASSERT_EQ(expected_names, names);
+
+ CloseArchive(handle);
+}
+
+static void AssertIterationOrder(const std::string_view prefix, const std::string_view suffix,
const std::vector<std::string>& expected_names_sorted) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -123,10 +138,10 @@
ZipEntry data;
std::vector<std::string> names;
- ZipString name;
+ std::string name;
for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
- names.push_back(std::string(reinterpret_cast<const char*>(name.name), name.name_length));
+ names.push_back(name);
}
// End of iteration.
@@ -142,30 +157,26 @@
static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
"b/d.txt"};
- AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
+ AssertIterationOrder("", "", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithPrefix) {
- ZipString prefix("b/");
static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
- AssertIterationOrder(&prefix, nullptr, kExpectedMatchesSorted);
+ AssertIterationOrder("b/", "", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithSuffix) {
- ZipString suffix(".txt");
static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/c.txt",
"b/d.txt"};
- AssertIterationOrder(nullptr, &suffix, kExpectedMatchesSorted);
+ AssertIterationOrder("", ".txt", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithPrefixAndSuffix) {
- ZipString prefix("b");
- ZipString suffix(".txt");
static const std::vector<std::string> kExpectedMatchesSorted = {"b.txt", "b/c.txt", "b/d.txt"};
- AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
+ AssertIterationOrder("b", ".txt", kExpectedMatchesSorted);
}
TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
@@ -173,12 +184,10 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
void* iteration_cookie;
- ZipString prefix("x");
- ZipString suffix("y");
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
ZipEntry data;
- ZipString name;
+ std::string name;
// End of iteration.
ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -191,9 +200,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry data;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
// Known facts about a.txt, from zipinfo -v.
ASSERT_EQ(63, data.offset);
@@ -204,9 +211,28 @@
ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
// An entry that doesn't exist. Should be a negative return code.
- ZipString absent_name;
- SetZipString(&absent_name, kNonexistentTxtName);
- ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
+ ASSERT_LT(FindEntry(handle, kNonexistentTxtName, &data), 0);
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_empty) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, "", &data));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, FindEntry_too_long) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ std::string very_long_name(65536, 'x');
+ ZipEntry data;
+ ASSERT_EQ(kInvalidEntryName, FindEntry(handle, very_long_name, &data));
CloseArchive(handle);
}
@@ -216,9 +242,9 @@
ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
- ZipString name;
+ std::string name;
ZipEntry data;
ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -233,9 +259,7 @@
// An entry that's deflated.
ZipEntry data;
- ZipString a_name;
- SetZipString(&a_name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, a_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
const uint32_t a_size = data.uncompressed_length;
ASSERT_EQ(a_size, kATxtContents.size());
uint8_t* buffer = new uint8_t[a_size];
@@ -244,9 +268,7 @@
delete[] buffer;
// An entry that's stored.
- ZipString b_name;
- SetZipString(&b_name, kBTxtName);
- ASSERT_EQ(0, FindEntry(handle, b_name, &data));
+ ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
const uint32_t b_size = data.uncompressed_length;
ASSERT_EQ(b_size, kBTxtContents.size());
buffer = new uint8_t[b_size];
@@ -298,12 +320,10 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
ZipEntry entry;
- ZipString empty_name;
- SetZipString(&empty_name, kEmptyTxtName);
- ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
uint8_t buffer[1];
ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -323,17 +343,15 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
sizeof(kAbZip) - 1));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
ZipEntry entry;
- ZipString ab_name;
- SetZipString(&ab_name, kAbTxtName);
- ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
ASSERT_EQ(kAbUncompressedSize, entry.uncompressed_length);
// Extract the entry to memory.
std::vector<uint8_t> buffer(kAbUncompressedSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
+ ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], static_cast<uint32_t>(buffer.size())));
// Extract the entry to a file.
TemporaryFile tmp_output_file;
@@ -370,7 +388,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
ZipArchiveHandle handle;
- ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
+ ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
}
TEST(ziparchive, ExtractToFile) {
@@ -385,9 +403,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
ZipEntry entry;
- ZipString name;
- SetZipString(&name, kATxtName);
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
// Assert that the first 8 bytes of the file haven't been clobbered.
@@ -416,16 +432,15 @@
ASSERT_EQ(0, fstat(fd, &sb));
// Memory map the file first and open the archive from the memory region.
- auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
+ auto file_map{
+ android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
ZipArchiveHandle handle;
ASSERT_EQ(0,
OpenArchiveFromMemory(file_map->data(), file_map->size(), zip_path.c_str(), &handle));
// Assert one entry can be found and extracted correctly.
- std::string BINARY_PATH("META-INF/com/google/android/update-binary");
- ZipString binary_path(BINARY_PATH.c_str());
ZipEntry binary_entry;
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+ ASSERT_EQ(0, FindEntry(handle, "META-INF/com/google/android/update-binary", &binary_entry));
TemporaryFile tmp_binary;
ASSERT_NE(-1, tmp_binary.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -434,9 +449,7 @@
static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
- ZipString name;
- SetZipString(&name, entry_name);
- ASSERT_EQ(0, FindEntry(handle, name, entry));
+ ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
std::unique_ptr<ZipArchiveStreamEntry> stream;
if (raw) {
stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -489,7 +502,8 @@
std::vector<uint8_t> cmp_data(entry.uncompressed_length);
ASSERT_EQ(entry.uncompressed_length, read_data.size());
- ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
+ ASSERT_EQ(
+ 0, ExtractToMemory(handle, &entry, cmp_data.data(), static_cast<uint32_t>(cmp_data.size())));
ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
CloseArchive(handle);
@@ -580,17 +594,13 @@
ASSERT_NE(-1, tmp_file.fd);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &zip_data[0], zip_data.size()));
ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle));
+ ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "ExtractEntryToMemory", &handle, false));
// This function expects a variant of kDataDescriptorZipFile, for look for
// an entry whose name is "name" and whose size is 12 (contents =
// "abdcdefghijk").
ZipEntry entry;
- ZipString name;
- std::string name_str = "name";
- SetZipString(&name, name_str);
-
- ASSERT_EQ(0, FindEntry(handle, name, &entry));
+ ASSERT_EQ(0, FindEntry(handle, "name", &entry));
ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
entry_out->resize(12);
@@ -688,7 +698,7 @@
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
kZipFileWithBrokenLfhSignature.size()));
ZipArchiveHandle handle;
- ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
+ ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle, false));
}
class VectorReader : public zip_archive::Reader {
@@ -738,8 +748,8 @@
};
TEST(ziparchive, Inflate) {
- const uint32_t compressed_length = kATxtContentsCompressed.size();
- const uint32_t uncompressed_length = kATxtContents.size();
+ const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
+ const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
const VectorReader reader(kATxtContentsCompressed);
{
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 981df3a..67279a6 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -30,9 +30,13 @@
#include "entry_name_utils-inl.h"
#include "zip_archive_common.h"
-#if !defined(powerof2)
-#define powerof2(x) ((((x)-1) & (x)) == 0)
-#endif
+#undef powerof2
+#define powerof2(x) \
+ ({ \
+ __typeof__(x) _x = (x); \
+ __typeof__(x) _x2; \
+ __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2 & _x) == 0); \
+ })
/* Zip compression methods we support */
enum {
@@ -126,7 +130,7 @@
return error_code;
}
-int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
uint32_t alignment = 0;
if (flags & kAlign32) {
flags &= ~kAlign32;
@@ -135,11 +139,11 @@
return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}
-int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}
-int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
uint32_t alignment = 0;
if (flags & kAlign32) {
flags &= ~kAlign32;
@@ -165,8 +169,8 @@
year = 80;
}
- *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
- *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+ *out_date = static_cast<uint16_t>((year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday);
+ *out_time = static_cast<uint16_t>(ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1);
}
static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
@@ -189,16 +193,22 @@
dst->compression_method = src.compression_method;
dst->last_mod_time = src.last_mod_time;
dst->last_mod_date = src.last_mod_date;
- dst->file_name_length = src.path.size();
+ DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
+ dst->file_name_length = static_cast<uint16_t>(src.path.size());
dst->extra_field_length = src.padding_length;
}
-int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
uint32_t alignment) {
if (state_ != State::kWritingZip) {
return kInvalidState;
}
+ // Can only have 16535 entries because of zip records.
+ if (files_.size() == std::numeric_limits<uint16_t>::max()) {
+ return HandleError(kIoError);
+ }
+
if (flags & kAlign32) {
return kInvalidAlign32Flag;
}
@@ -206,10 +216,17 @@
if (powerof2(alignment) == 0) {
return kInvalidAlignment;
}
+ if (alignment > std::numeric_limits<uint16_t>::max()) {
+ return kInvalidAlignment;
+ }
FileEntry file_entry = {};
file_entry.local_file_header_offset = current_offset_;
file_entry.path = path;
+ // No support for larger than 4GB files.
+ if (file_entry.local_file_header_offset > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
file_entry.path.size())) {
@@ -230,13 +247,24 @@
ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
- std::vector<char> zero_padding;
+ // prepare a pre-zeroed memory page in case when we need to pad some aligned data.
+ static constexpr auto kPageSize = 4096;
+ static constexpr char kSmallZeroPadding[kPageSize] = {};
+ // use this buffer if our preallocated one is too small
+ std::vector<char> zero_padding_big;
+ const char* zero_padding = nullptr;
+
if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
- uint16_t padding = alignment - (offset % alignment);
+ uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
file_entry.padding_length = padding;
offset += padding;
- zero_padding.resize(padding, 0);
+ if (padding <= std::size(kSmallZeroPadding)) {
+ zero_padding = kSmallZeroPadding;
+ } else {
+ zero_padding_big.resize(padding, 0);
+ zero_padding = zero_padding_big.data();
+ }
}
LocalFileHeader header = {};
@@ -248,11 +276,11 @@
return HandleError(kIoError);
}
- if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
+ if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
return HandleError(kIoError);
}
- if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
+ if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
file_) != file_entry.padding_length) {
return HandleError(kIoError);
}
@@ -310,7 +338,8 @@
}
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
return kNoError;
}
@@ -318,25 +347,31 @@
if (state_ != State::kWritingEntry) {
return HandleError(kInvalidState);
}
+ // Need to be able to mark down data correctly.
+ if (len + static_cast<uint64_t>(current_file_entry_.uncompressed_size) >
+ std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ uint32_t len32 = static_cast<uint32_t>(len);
int32_t result = kNoError;
if (current_file_entry_.compression_method & kCompressDeflated) {
- result = CompressBytes(¤t_file_entry_, data, len);
+ result = CompressBytes(¤t_file_entry_, data, len32);
} else {
- result = StoreBytes(¤t_file_entry_, data, len);
+ result = StoreBytes(¤t_file_entry_, data, len32);
}
if (result != kNoError) {
return result;
}
- current_file_entry_.crc32 =
- crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
- current_file_entry_.uncompressed_size += len;
+ current_file_entry_.crc32 = static_cast<uint32_t>(
+ crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len32));
+ current_file_entry_.uncompressed_size += len32;
return kNoError;
}
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
CHECK(state_ == State::kWritingEntry);
if (fwrite(data, 1, len, file_) != len) {
@@ -347,7 +382,7 @@
return kNoError;
}
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
CHECK(state_ == State::kWritingEntry);
CHECK(z_stream_);
CHECK(z_stream_->next_out != nullptr);
@@ -375,7 +410,8 @@
// Reset the output buffer for the next input.
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
}
}
return kNoError;
@@ -400,7 +436,8 @@
current_offset_ += write_bytes;
z_stream_->next_out = buffer_.data();
- z_stream_->avail_out = buffer_.size();
+ DCHECK_EQ(buffer_.size(), kBufSize);
+ z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
}
if (zerr != Z_STREAM_END) {
return HandleError(kZlibError);
@@ -418,6 +455,11 @@
return kNoError;
}
+bool ZipWriter::ShouldUseDataDescriptor() const {
+ // Only use a trailing "data descriptor" if the output isn't seekable.
+ return !seekable_;
+}
+
int32_t ZipWriter::FinishEntry() {
if (state_ != State::kWritingEntry) {
return kInvalidState;
@@ -430,7 +472,7 @@
}
}
- if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+ if (ShouldUseDataDescriptor()) {
// Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
// If this file is not seekable, or if the data is compressed, write a DataDescriptor.
const uint32_t sig = DataDescriptor::kOptSignature;
@@ -478,7 +520,7 @@
for (FileEntry& file : files_) {
CentralDirectoryRecord cdr = {};
cdr.record_signature = CentralDirectoryRecord::kSignature;
- if ((file.compression_method & kCompressDeflated) || !seekable_) {
+ if (ShouldUseDataDescriptor()) {
cdr.gpb_flags |= kGPBDDFlagMask;
}
cdr.compression_method = file.compression_method;
@@ -487,7 +529,11 @@
cdr.crc32 = file.crc32;
cdr.compressed_size = file.compressed_size;
cdr.uncompressed_size = file.uncompressed_size;
- cdr.file_name_length = file.path.size();
+ // Checked in IsValidEntryName.
+ DCHECK_LE(file.path.size(), std::numeric_limits<uint16_t>::max());
+ cdr.file_name_length = static_cast<uint16_t>(file.path.size());
+ // Checked in StartAlignedEntryWithTime.
+ DCHECK_LE(file.local_file_header_offset, std::numeric_limits<uint32_t>::max());
cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
return HandleError(kIoError);
@@ -504,10 +550,15 @@
er.eocd_signature = EocdRecord::kSignature;
er.disk_num = 0;
er.cd_start_disk = 0;
- er.num_records_on_disk = files_.size();
- er.num_records = files_.size();
- er.cd_size = current_offset_ - startOfCdr;
- er.cd_start_offset = startOfCdr;
+ // Checked when adding entries.
+ DCHECK_LE(files_.size(), std::numeric_limits<uint16_t>::max());
+ er.num_records_on_disk = static_cast<uint16_t>(files_.size());
+ er.num_records = static_cast<uint16_t>(files_.size());
+ if (current_offset_ > std::numeric_limits<uint32_t>::max()) {
+ return HandleError(kIoError);
+ }
+ er.cd_size = static_cast<uint32_t>(current_offset_ - startOfCdr);
+ er.cd_start_offset = static_cast<uint32_t>(startOfCdr);
if (fwrite(&er, sizeof(er), 1, file_) != 1) {
return HandleError(kIoError);
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index c284273..d324d4b 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -62,7 +62,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.has_data_descriptor);
EXPECT_EQ(strlen(expected), data.compressed_length);
@@ -95,19 +95,19 @@
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(2u, data.compressed_length);
ASSERT_EQ(2u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(3u, data.compressed_length);
ASSERT_EQ(3u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file/file2.txt", &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(0u, data.compressed_length);
EXPECT_EQ(0u, data.uncompressed_length);
@@ -129,7 +129,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
CloseArchive(handle);
@@ -163,7 +163,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0x03);
struct tm mod = data.GetModificationTime();
@@ -191,7 +191,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
CloseArchive(handle);
@@ -213,7 +213,7 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "align.txt", &data));
EXPECT_EQ(0, data.offset & 0xfff);
struct tm mod = data.GetModificationTime();
@@ -241,8 +241,9 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
+ EXPECT_EQ(0u, data.has_data_descriptor);
ASSERT_EQ(4u, data.uncompressed_length);
ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
@@ -257,7 +258,7 @@
std::vector<uint8_t> buffer(kBufSize);
size_t prev = 1;
for (size_t i = 0; i < kBufSize; i++) {
- buffer[i] = i + prev;
+ buffer[i] = static_cast<uint8_t>(i + prev);
prev = i;
}
@@ -273,13 +274,14 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
EXPECT_EQ(kCompressDeflated, data.method);
EXPECT_EQ(kBufSize, data.uncompressed_length);
std::vector<uint8_t> decompress(kBufSize);
memset(decompress.data(), 0, kBufSize);
- ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+ ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(),
+ static_cast<uint32_t>(decompress.size())));
EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
<< "Input buffer and output buffer are different.";
@@ -339,17 +341,40 @@
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
ZipEntry data;
- ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
- ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+ ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
- ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+ ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
CloseArchive(handle);
}
+TEST_F(zipwriter, WriteToUnseekableFile) {
+ const char* expected = "hello";
+ ZipWriter writer(file_);
+ writer.seekable_ = false;
+
+ ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+ EXPECT_EQ(kCompressStored, data.method);
+ EXPECT_EQ(1u, data.has_data_descriptor);
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ ASSERT_EQ(strlen(expected), data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
+ CloseArchive(handle);
+}
+
TEST_F(zipwriter, TruncateFileAfterBackup) {
ZipWriter writer(file_);
@@ -391,7 +416,7 @@
actual.resize(expected.size());
uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
- if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
+ if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
return ::testing::AssertionFailure() << "failed to extract entry";
}
diff --git a/llkd/README.md b/llkd/README.md
index e5be850..191f988 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -44,7 +44,8 @@
ABA detection since forward scheduling progress is allowed, thus the condition
for the symbols are:
-- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+ /proc/__pid__/stack.
- The __symbol__ should be rare and short lived enough that on a typical
system the function is seen at most only once in a sample over the timeout
period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
@@ -85,10 +86,23 @@
Android Properties
------------------
-Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
+The following are the Android Properties llkd respond to.
+*prop*_ms named properties are in milliseconds.
+Properties that use comma (*,*) separator for lists, use a leading separator to
+preserve default and add or subtract entries with (*optional*) plus (*+*) and
+minus (*-*) prefixes respectively.
+For these lists, the string "*false*" is synonymous with an *empty* list,
+and *blank* or *missing* resorts to the specified *default* value.
#### ro.config.low_ram
-default false, if true do not sysrq t (dump all threads).
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do sysrq t (dump all threads).
#### ro.llk.enable
default false, allow live-lock daemon to be enabled.
@@ -121,14 +135,14 @@
#### ro.llk.stack.timeout_ms
default ro.llk.timeout_ms,
checking for persistent stack symbols maximum timelimit.
-Only active on userdebug and eng builds.
+Only active on userdebug or eng builds.
#### ro.llk.check_ms
default 2 minutes samples of threads for D or Z.
#### ro.llk.stack
-default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
-The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
+comma separated list of kernel symbols.
Look for kernel stack symbols that if ever persistently present can
indicate a subsystem is locked up.
Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -136,32 +150,36 @@
should be exceptionally rare and fleeting.
One must be convinced that it is virtually *impossible* for symbol to show up
persistently in all samples of the stack.
-Only active on userdebug and eng builds.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
#### ro.llk.blacklist.process
default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
-The string "*false*" is the equivalent to an *empty* list.
Do not watch these processes. A process can be comm, cmdline or pid reference.
NB: automated default here can be larger than the current maximum property
size of 92.
NB: false is a very very very unlikely process to want to blacklist.
#### ro.llk.blacklist.parent
-default 0,2 (kernel and [kthreadd]).
-The string "*false*" is the equivalent to an *empty* list.
+default 0,2,adbd&[setsid] (kernel, [kthreadd] and adbd *only for zombie setsid*).
Do not watch processes that have this parent.
-A parent process can be comm, cmdline or pid reference.
+An ampersand (*&*) separator is used to specify that the parent is ignored
+only in combination with the target child process.
+Ampersand was selected because it is never part of a process name,
+however a setprop in the shell requires it to be escaped or quoted;
+init rc file where this is normally specified does not have this issue.
+A parent or target processes can be specified as comm, cmdline or pid reference.
#### ro.llk.blacklist.uid
default *empty* or false, comma separated list of uid numbers or names.
-The string "*false*" is the equivalent to an *empty* list.
Do not watch processes that match this uid.
#### ro.llk.blacklist.process.stack
-default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
-The string "*false*" is the equivalent to an *empty* list.
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
This subset of processes are not monitored for live lock stack signatures.
Also prevents the sepolicy violation associated with processes that block
ptrace, as these can not be checked anyways.
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 2c62fca..3586ca1 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -35,6 +35,8 @@
#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT true
#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
#define LLK_MLOCKALL_DEFAULT true
#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
@@ -48,12 +50,13 @@
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages"
+#define LLK_CHECK_STACK_DEFAULT \
+ "cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable"
#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
#define LLK_BLACKLIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
#define LLK_BLACKLIST_PARENT_PROPERTY "ro.llk.blacklist.parent"
-#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd]"
+#define LLK_BLACKLIST_PARENT_DEFAULT "0,2,[kthreadd],adbd&[setsid]"
#define LLK_BLACKLIST_UID_PROPERTY "ro.llk.blacklist.uid"
#define LLK_BLACKLIST_UID_DEFAULT ""
#define LLK_BLACKLIST_STACK_PROPERTY "ro.llk.blacklist.process.stack"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 2727aab..b26ad4d 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -24,6 +24,7 @@
#include <pwd.h> // getpwuid()
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/cdefs.h> // ___STRING, __predict_true() and _predict_false()
#include <sys/mman.h> // mlockall()
#include <sys/prctl.h>
@@ -85,6 +86,7 @@
milliseconds llkCheckMs; // checking interval to inspect any
// persistent live-locked states
bool llkLowRam; // ro.config.low_ram
+bool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT; // sysrq stack trace dump
bool khtEnable = LLK_ENABLE_DEFAULT; // [khungtaskd] panic
// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
// Provides a wide angle of margin b/c khtTimeout is also its granularity.
@@ -106,6 +108,9 @@
// list of parent pids, comm or cmdline names to skip. default:
// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied
std::unordered_set<std::string> llkBlacklistParent;
+// list of parent and target processes to skip. default:
+// adbd *and* [setsid]
+std::unordered_map<std::string, std::unordered_set<std::string>> llkBlacklistParentAndChild;
// list of uids, and uid names, to skip, default nothing
std::unordered_set<std::string> llkBlacklistUid;
#ifdef __PTRACE_ENABLED__
@@ -509,8 +514,8 @@
return android::base::Trim(content) == string;
}
-void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message = "") {
+ if (!message.empty()) LOG(ERROR) << message;
auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
if (sysrqTriggerFd < 0) {
// DYB
@@ -518,19 +523,37 @@
// The answer to life, the universe and everything
::exit(42);
// NOTREACHED
+ return;
}
- ::sync();
+ // Wish could ::sync() here, if storage is locked up, we will not continue.
if (dump) {
// Show all locks that are held
android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
// This can trigger hardware watchdog, that is somewhat _ok_.
// But useless if pstore configured for <256KB, low ram devices ...
- if (!llkLowRam) {
+ if (llkEnableSysrqT) {
android::base::WriteStringToFd("t", sysrqTriggerFd);
+ // Show all locks that are held (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
}
::usleep(200000); // let everything settle
}
- llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
+ // SysRq message matches kernel format, and propagates through bootstat
+ // ultimately to the boot reason into panic,livelock,<state>.
+ llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+ "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+ "/dev/kmsg");
+ // Because panic is such a serious thing to do, let us
+ // make sure that the tid being inspected still exists!
+ auto piddir = procdir + std::to_string(tid) + "/stat";
+ if (access(piddir.c_str(), F_OK) != 0) {
+ PLOG(WARNING) << piddir;
+ return;
+ }
android::base::WriteStringToFd("c", sysrqTriggerFd);
// NOTREACHED
// DYB
@@ -542,7 +565,9 @@
}
void llkAlarmHandler(int) {
- llkPanicKernel(false, ::getpid(), "alarm");
+ LOG(FATAL) << "alarm";
+ // NOTREACHED
+ llkPanicKernel(true, ::getpid(), "alarm");
}
milliseconds GetUintProperty(const std::string& key, milliseconds def) {
@@ -595,18 +620,38 @@
std::string llkFormat(const std::unordered_set<std::string>& blacklist) {
std::string ret;
- for (auto entry : blacklist) {
- if (ret.size()) {
- ret += ",";
- }
+ for (const auto& entry : blacklist) {
+ if (!ret.empty()) ret += ",";
ret += entry;
}
return ret;
}
+std::string llkFormat(
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist,
+ bool leading_comma = false) {
+ std::string ret;
+ for (const auto& entry : blacklist) {
+ for (const auto& target : entry.second) {
+ if (leading_comma || !ret.empty()) ret += ",";
+ ret += entry.first + "&" + target;
+ }
+ }
+ return ret;
+}
+
+// This function parses the properties as a list, incorporating the supplied
+// default. A leading comma separator means preserve the defaults and add
+// entries (with an optional leading + sign), or removes entries with a leading
+// - sign.
+//
// We only officially support comma separators, but wetware being what they
// are will take some liberty and I do not believe they should be punished.
-std::unordered_set<std::string> llkSplit(const std::string& s) {
+std::unordered_set<std::string> llkSplit(const std::string& prop, const std::string& def) {
+ auto s = android::base::GetProperty(prop, def);
+ constexpr char separators[] = ", \t:;";
+ if (!s.empty() && (s != def) && strchr(separators, s[0])) s = def + s;
+
std::unordered_set<std::string> result;
// Special case, allow boolean false to empty the list, otherwise expected
@@ -616,9 +661,29 @@
size_t base = 0;
while (s.size() > base) {
- auto found = s.find_first_of(", \t:", base);
- // Only emplace content, empty entries are not an option
- if (found != base) result.emplace(s.substr(base, found - base));
+ auto found = s.find_first_of(separators, base);
+ // Only emplace unique content, empty entries are not an option
+ if (found != base) {
+ switch (s[base]) {
+ case '-':
+ ++base;
+ if (base >= s.size()) break;
+ if (base != found) {
+ auto have = result.find(s.substr(base, found - base));
+ if (have != result.end()) result.erase(have);
+ }
+ break;
+ case '+':
+ ++base;
+ if (base >= s.size()) break;
+ if (base == found) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; following for clang)
+ FALLTHROUGH_INTENDED;
+ default:
+ result.emplace(s.substr(base, found - base));
+ break;
+ }
+ }
if (found == s.npos) break;
base = found + 1;
}
@@ -627,13 +692,42 @@
bool llkSkipName(const std::string& name,
const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
- if ((name.size() == 0) || (blacklist.size() == 0)) {
- return false;
- }
+ if (name.empty() || blacklist.empty()) return false;
return blacklist.find(name) != blacklist.end();
}
+bool llkSkipProc(proc* procp,
+ const std::unordered_set<std::string>& blacklist = llkBlacklistProcess) {
+ if (!procp) return false;
+ if (llkSkipName(std::to_string(procp->pid), blacklist)) return true;
+ if (llkSkipName(procp->getComm(), blacklist)) return true;
+ if (llkSkipName(procp->getCmdline(), blacklist)) return true;
+ if (llkSkipName(android::base::Basename(procp->getCmdline()), blacklist)) return true;
+ return false;
+}
+
+const std::unordered_set<std::string>& llkSkipName(
+ const std::string& name,
+ const std::unordered_map<std::string, std::unordered_set<std::string>>& blacklist) {
+ static const std::unordered_set<std::string> empty;
+ if (name.empty() || blacklist.empty()) return empty;
+ auto found = blacklist.find(name);
+ if (found == blacklist.end()) return empty;
+ return found->second;
+}
+
+bool llkSkipPproc(proc* pprocp, proc* procp,
+ const std::unordered_map<std::string, std::unordered_set<std::string>>&
+ blacklist = llkBlacklistParentAndChild) {
+ if (!pprocp || !procp || blacklist.empty()) return false;
+ if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), blacklist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), blacklist))) return true;
+ if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), blacklist))) return true;
+ return llkSkipProc(procp,
+ llkSkipName(android::base::Basename(pprocp->getCmdline()), blacklist));
+}
+
bool llkSkipPid(pid_t pid) {
return llkSkipName(std::to_string(pid), llkBlacklistProcess);
}
@@ -709,25 +803,24 @@
}
// Don't check process that are known to block ptrace, save sepolicy noise.
- if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
- if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
- if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
- if (llkSkipName(android::base::Basename(procp->getCmdline()), llkBlacklistStack)) return false;
-
+ if (llkSkipProc(procp, llkBlacklistStack)) return false;
auto kernel_stack = ReadFile(piddir + "/stack");
if (kernel_stack.empty()) {
- LOG(INFO) << piddir << "/stack empty comm=" << procp->getComm()
- << " cmdline=" << procp->getCmdline();
+ LOG(VERBOSE) << piddir << "/stack empty comm=" << procp->getComm()
+ << " cmdline=" << procp->getCmdline();
return false;
}
// A scheduling incident that should not reset count_stack
if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
char idx = -1;
char match = -1;
+ std::string matched_stack_symbol = "<unknown>";
for (const auto& stack : llkCheckStackSymbols) {
if (++idx < 0) break;
- if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+ if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+ (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
match = idx;
+ matched_stack_symbol = stack;
break;
}
}
@@ -738,7 +831,9 @@
}
if (match == char(-1)) return false;
procp->count_stack += llkCycle;
- return procp->count_stack >= llkStateTimeoutMs[llkStateStack];
+ if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;
+ LOG(WARNING) << "Found " << matched_stack_symbol << " in stack for pid " << procp->pid;
+ return true;
}
#endif
@@ -754,12 +849,12 @@
// but if there are problems we assume at least a few
// samples of reads occur before we take any real action.
std::string schedString = ReadFile(piddir + "/sched");
- if (schedString.size() == 0) {
+ if (schedString.empty()) {
// /schedstat is not as standardized, but in 3.1+
// Android devices, the third field is nr_switches
// from /sched:
schedString = ReadFile(piddir + "/schedstat");
- if (schedString.size() == 0) {
+ if (schedString.empty()) {
return;
}
auto val = static_cast<unsigned long long>(-1);
@@ -798,6 +893,7 @@
void llkLogConfig(void) {
LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+ << LLK_ENABLE_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\n"
<< LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
<< KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
<< LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
@@ -816,7 +912,8 @@
<< LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
#endif
<< LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
- << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
+ << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent)
+ << llkFormat(llkBlacklistParentAndChild, true) << "\n"
<< LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
}
@@ -892,6 +989,7 @@
ms -= llkCycle;
auto myPid = ::getpid();
auto myTid = ::gettid();
+ auto dump = true;
for (auto dp = llkTopDirectory.read(); dp != nullptr; dp = llkTopDirectory.read()) {
std::string piddir;
@@ -915,7 +1013,7 @@
// Get the process stat
std::string stat = ReadFile(piddir + "/stat");
- if (stat.size() == 0) {
+ if (stat.empty()) {
continue;
}
unsigned tid = -1;
@@ -990,7 +1088,8 @@
break;
}
- if (llkSkipName(procp->getComm())) {
+ auto process_comm = procp->getComm();
+ if (llkSkipName(process_comm)) {
continue;
}
if (llkSkipName(procp->getCmdline())) {
@@ -1004,11 +1103,11 @@
if (pprocp == nullptr) {
pprocp = llkTidAlloc(ppid, ppid, 0, "", 0, '?');
}
- if ((pprocp != nullptr) &&
- (llkSkipName(pprocp->getComm(), llkBlacklistParent) ||
- llkSkipName(pprocp->getCmdline(), llkBlacklistParent) ||
- llkSkipName(android::base::Basename(pprocp->getCmdline()), llkBlacklistParent))) {
- break;
+ if (pprocp) {
+ if (llkSkipPproc(pprocp, procp)) break;
+ if (llkSkipProc(pprocp, llkBlacklistParent)) break;
+ } else {
+ if (llkSkipName(std::to_string(ppid), llkBlacklistParent)) break;
}
if ((llkBlacklistUid.size() != 0) && llkSkipUid(procp->getUid())) {
@@ -1025,7 +1124,7 @@
stuck = true;
} else if (procp->count != 0ms) {
LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm();
+ << pid << "->" << tid << ' ' << process_comm;
}
}
if (!stuck) continue;
@@ -1033,7 +1132,7 @@
if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
if (procp->count != 0ms) {
LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm();
+ << pid << "->" << tid << ' ' << process_comm;
}
continue;
}
@@ -1061,7 +1160,7 @@
break;
}
LOG(WARNING) << "Z " << llkFormat(procp->count) << ' ' << ppid << "->"
- << pid << "->" << tid << ' ' << procp->getComm() << " [kill]";
+ << pid << "->" << tid << ' ' << process_comm << " [kill]";
if ((llkKillOneProcess(pprocp, procp) >= 0) ||
(llkKillOneProcess(ppid, procp) >= 0)) {
continue;
@@ -1078,7 +1177,7 @@
// kernel (worse).
default:
LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid
- << "->" << tid << ' ' << procp->getComm() << " [kill]";
+ << "->" << tid << ' ' << process_comm << " [kill]";
if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
(llkKillOneProcess(pid, state, tid) >= 0) ||
(llkKillOneProcess(procp, procp) >= 0) ||
@@ -1089,10 +1188,13 @@
}
}
// We are here because we have confirmed kernel live-lock
- LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
- << "->" << tid << ' ' << procp->getComm() << " [panic]";
- llkPanicKernel(true, tid,
- (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
+ const auto message = state + " "s + llkFormat(procp->count) + " " +
+ std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+ std::to_string(tid) + " " + process_comm + " [panic]";
+ llkPanicKernel(dump, tid,
+ (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+ message);
+ dump = false;
}
LOG(VERBOSE) << "+closedir()";
}
@@ -1104,21 +1206,15 @@
if (!p->second.updated) {
IF_ALOG(LOG_VERBOSE, LOG_TAG) {
std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);
- if (ppidCmdline.size()) {
- ppidCmdline = "(" + ppidCmdline + ")";
- }
+ if (!ppidCmdline.empty()) ppidCmdline = "(" + ppidCmdline + ")";
std::string pidCmdline;
if (p->second.pid != p->second.tid) {
pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());
- if (pidCmdline.size()) {
- pidCmdline = "(" + pidCmdline + ")";
- }
+ if (!pidCmdline.empty()) pidCmdline = "(" + pidCmdline + ")";
}
std::string tidCmdline =
llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());
- if (tidCmdline.size()) {
- tidCmdline = "(" + tidCmdline + ")";
- }
+ if (!tidCmdline.empty()) tidCmdline = "(" + tidCmdline + ")";
LOG(VERBOSE) << "thread " << p->second.ppid << ppidCmdline << "->" << p->second.pid
<< pidCmdline << "->" << p->second.tid << tidCmdline << " removed";
}
@@ -1149,13 +1245,22 @@
return duration_cast<milliseconds>(llkCheck()).count();
}
+bool llkCheckEng(const std::string& property) {
+ return android::base::GetProperty(property, "eng") == "eng";
+}
+
bool llkInit(const char* threadname) {
auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
- if (!LLK_ENABLE_DEFAULT && debuggable) {
- llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
- khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+ llkEnableSysrqT &= !llkLowRam;
+ if (debuggable) {
+ llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+ if (!LLK_ENABLE_DEFAULT) { // NB: default is currently true ...
+ llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+ khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+ }
}
+ llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
if (llkEnable && !llkTopDirectory.reset(procdir)) {
// Most likely reason we could be here is llkd was started
@@ -1186,13 +1291,11 @@
llkValidate(); // validate all (effectively minus llkTimeoutMs)
#ifdef __PTRACE_ENABLED__
if (debuggable) {
- llkCheckStackSymbols = llkSplit(
- android::base::GetProperty(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT));
+ llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);
}
std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
- llkBlacklistStack = llkSplit(
- android::base::GetProperty(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack));
+ llkBlacklistStack = llkSplit(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack);
#endif
std::string defaultBlacklistProcess(
std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
@@ -1204,17 +1307,34 @@
for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {
defaultBlacklistProcess += ",[watchdog/" + std::to_string(cpu) + "]";
}
- defaultBlacklistProcess =
- android::base::GetProperty(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
- llkBlacklistProcess = llkSplit(defaultBlacklistProcess);
+ llkBlacklistProcess = llkSplit(LLK_BLACKLIST_PROCESS_PROPERTY, defaultBlacklistProcess);
if (!llkSkipName("[khungtaskd]")) { // ALWAYS ignore as special
llkBlacklistProcess.emplace("[khungtaskd]");
}
- llkBlacklistParent = llkSplit(android::base::GetProperty(
- LLK_BLACKLIST_PARENT_PROPERTY, std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
- "," LLK_BLACKLIST_PARENT_DEFAULT));
- llkBlacklistUid =
- llkSplit(android::base::GetProperty(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT));
+ llkBlacklistParent = llkSplit(LLK_BLACKLIST_PARENT_PROPERTY,
+ std::to_string(kernelPid) + "," + std::to_string(kthreaddPid) +
+ "," LLK_BLACKLIST_PARENT_DEFAULT);
+ // derive llkBlacklistParentAndChild by moving entries with '&' from above
+ for (auto it = llkBlacklistParent.begin(); it != llkBlacklistParent.end();) {
+ auto pos = it->find('&');
+ if (pos == std::string::npos) {
+ ++it;
+ continue;
+ }
+ auto parent = it->substr(0, pos);
+ auto child = it->substr(pos + 1);
+ it = llkBlacklistParent.erase(it);
+
+ auto found = llkBlacklistParentAndChild.find(parent);
+ if (found == llkBlacklistParentAndChild.end()) {
+ llkBlacklistParentAndChild.emplace(std::make_pair(
+ std::move(parent), std::unordered_set<std::string>({std::move(child)})));
+ } else {
+ found->second.emplace(std::move(child));
+ }
+ }
+
+ llkBlacklistUid = llkSplit(LLK_BLACKLIST_UID_PROPERTY, LLK_BLACKLIST_UID_DEFAULT);
// internal watchdog
::signal(SIGALRM, llkAlarmHandler);
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index f54932b..96079cc 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -17,6 +17,7 @@
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -87,7 +88,8 @@
execute("stop llkd-1");
rest();
std::string setprop("setprop ");
- execute((setprop + LLK_CHECK_STACK_PROPERTY + " SyS_openat").c_str());
+ // Manually check that SyS_openat is _added_ to the list when restarted
+ execute((setprop + LLK_CHECK_STACK_PROPERTY + " ,SyS_openat").c_str());
rest();
execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
rest();
@@ -332,3 +334,37 @@
unlink(stack_pipe_file);
}
+
+// b/120983740
+TEST(llkd, adbd_and_setsid) {
+ if (checkKill("kernel_panic,sysrq,livelock,zombie")) {
+ return;
+ }
+ const auto period = llkdSleepPeriod('S');
+
+ // expect llkd.zombie to trigger, but not for adbd&[setsid]
+ // Create a Persistent Zombie setsid Process
+ pid_t child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ if (!child_pid) {
+ prctl(PR_SET_NAME, "adbd");
+ auto zombie_pid = fork();
+ ASSERT_LE(0, zombie_pid);
+ if (!zombie_pid) {
+ prctl(PR_SET_NAME, "setsid");
+ sleep(1);
+ exit(0);
+ }
+ sleep(period.count());
+ exit(42);
+ }
+
+ // Reverse of waitForPid, do _not_ expect kill
+ int wstatus;
+ ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));
+ EXPECT_TRUE(WIFEXITED(wstatus));
+ if (WIFEXITED(wstatus)) {
+ EXPECT_EQ(42, WEXITSTATUS(wstatus));
+ }
+ ASSERT_FALSE(WIFSIGNALED(wstatus)) << "[ INFO ] signo=" << WTERMSIG(wstatus);
+}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 903d0e2..4c5ca03 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -5,6 +5,8 @@
shared_libs: [
"libcutils",
"liblog",
+ "libprocessgroup",
+ "libpsi",
],
static_libs: [
"libstatslogc",
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
index 7c2cd18..065c6db 100644
--- a/lmkd/event.logtags
+++ b/lmkd/event.logtags
@@ -35,4 +35,4 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
# for meminfo logs
-10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
+10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index e72d159..59377dd 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -50,7 +50,7 @@
typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
/* Get LMKD packet command */
-inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+static inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
return (enum lmk_cmd)ntohl(pack[0]);
}
@@ -64,8 +64,8 @@
* For LMK_TARGET packet get target_idx-th payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
- int target_idx, struct lmk_target *target) {
+static inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet, int target_idx,
+ struct lmk_target* target) {
target->minfree = ntohl(packet[target_idx * 2 + 1]);
target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
}
@@ -74,9 +74,8 @@
* Prepare LMK_TARGET packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
- struct lmk_target *targets,
- size_t target_cnt) {
+static inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet, struct lmk_target* targets,
+ size_t target_cnt) {
int idx = 0;
packet[idx++] = htonl(LMK_TARGET);
while (target_cnt) {
@@ -99,8 +98,7 @@
* For LMK_PROCPRIO packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
params->pid = (pid_t)ntohl(packet[1]);
params->uid = (uid_t)ntohl(packet[2]);
params->oomadj = ntohl(packet[3]);
@@ -110,8 +108,7 @@
* Prepare LMK_PROCPRIO packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet, struct lmk_procprio* params) {
packet[0] = htonl(LMK_PROCPRIO);
packet[1] = htonl(params->pid);
packet[2] = htonl(params->uid);
@@ -128,8 +125,8 @@
* For LMK_PROCREMOVE packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
- struct lmk_procremove *params) {
+static inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procremove* params) {
params->pid = (pid_t)ntohl(packet[1]);
}
@@ -137,8 +134,8 @@
* Prepare LMK_PROCREMOVE packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
- struct lmk_procprio *params) {
+static inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio* params) {
packet[0] = htonl(LMK_PROCREMOVE);
packet[1] = htonl(params->pid);
return 2 * sizeof(int);
@@ -148,7 +145,7 @@
* Prepare LMK_PROCPURGE packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
+static inline size_t lmkd_pack_set_procpurge(LMKD_CTRL_PACKET packet) {
packet[0] = htonl(LMK_PROCPURGE);
return sizeof(int);
}
@@ -163,8 +160,8 @@
* For LMK_GETKILLCNT packet get its payload.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
- struct lmk_getkillcnt *params) {
+static inline void lmkd_pack_get_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt* params) {
params->min_oomadj = ntohl(packet[1]);
params->max_oomadj = ntohl(packet[2]);
}
@@ -173,8 +170,8 @@
* Prepare LMK_GETKILLCNT packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
- struct lmk_getkillcnt *params) {
+static inline size_t lmkd_pack_set_getkillcnt(LMKD_CTRL_PACKET packet,
+ struct lmk_getkillcnt* params) {
packet[0] = htonl(LMK_GETKILLCNT);
packet[1] = htonl(params->min_oomadj);
packet[2] = htonl(params->max_oomadj);
@@ -185,7 +182,7 @@
* Prepare LMK_GETKILLCNT reply packet and return packet size in bytes.
* Warning: no checks performed, caller should ensure valid parameters.
*/
-inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
+static inline size_t lmkd_pack_set_getkillcnt_repl(LMKD_CTRL_PACKET packet, int kill_cnt) {
packet[0] = htonl(LMK_GETKILLCNT);
packet[1] = htonl(kill_cnt);
return 2 * sizeof(int);
diff --git a/lmkd/libpsi/Android.bp b/lmkd/libpsi/Android.bp
new file mode 100644
index 0000000..8a97094
--- /dev/null
+++ b/lmkd/libpsi/Android.bp
@@ -0,0 +1,22 @@
+cc_library_headers {
+ name: "libpsi_headers",
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libpsi",
+ srcs: ["psi.c"],
+ shared_libs: [
+ "liblog"
+ ],
+ header_libs: [
+ "libpsi_headers",
+ ],
+ export_header_lib_headers: [
+ "libpsi_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/lmkd/libpsi/OWNERS b/lmkd/libpsi/OWNERS
new file mode 100644
index 0000000..b15bb48
--- /dev/null
+++ b/lmkd/libpsi/OWNERS
@@ -0,0 +1 @@
+surenb@google.com
diff --git a/lmkd/libpsi/include/psi/psi.h b/lmkd/libpsi/include/psi/psi.h
new file mode 100644
index 0000000..cd49e8b
--- /dev/null
+++ b/lmkd/libpsi/include/psi/psi.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ANDROID_PSI_H__
+#define __ANDROID_PSI_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+enum psi_stall_type {
+ PSI_SOME,
+ PSI_FULL,
+ PSI_TYPE_COUNT
+};
+
+/*
+ * Initializes psi monitor.
+ * stall_type, threshold_us and window_us are monitor parameters
+ * When successful, the function returns file descriptor that can
+ * be used with poll/epoll syscalls to wait for EPOLLPRI events.
+ * When unsuccessful, the function returns -1 and errno is set
+ * appropriately.
+ */
+int init_psi_monitor(enum psi_stall_type stall_type,
+ int threshold_us, int window_us);
+
+/*
+ * Registers psi monitor file descriptor fd on the epoll instance
+ * referred to by the file descriptor epollfd.
+ * data parameter will be associated with event's epoll_data.ptr
+ * member.
+ */
+int register_psi_monitor(int epollfd, int fd, void* data);
+
+/*
+ * Unregisters psi monitor file descriptor fd from the epoll instance
+ * referred to by the file descriptor epollfd.
+ */
+int unregister_psi_monitor(int epollfd, int fd);
+
+/*
+ * Destroys psi monitor.
+ * fd is the file descriptor returned by psi monitor initialization
+ * routine.
+ * Note that if user process exits without calling this routine
+ * kernel will destroy the monitor as its lifetime is linked to
+ * the file descriptor.
+ */
+void destroy_psi_monitor(int fd);
+
+__END_DECLS
+
+#endif // __ANDROID_PSI_H__
diff --git a/lmkd/libpsi/psi.c b/lmkd/libpsi/psi.c
new file mode 100644
index 0000000..f4d5d18
--- /dev/null
+++ b/lmkd/libpsi/psi.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libpsi"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+
+#include <log/log.h>
+#include "psi/psi.h"
+
+#define PSI_MON_FILE_MEMORY "/proc/pressure/memory"
+
+static const char* stall_type_name[] = {
+ "some",
+ "full",
+};
+
+int init_psi_monitor(enum psi_stall_type stall_type,
+ int threshold_us, int window_us) {
+ int fd;
+ int res;
+ char buf[256];
+
+ fd = TEMP_FAILURE_RETRY(open(PSI_MON_FILE_MEMORY, O_WRONLY | O_CLOEXEC));
+ if (fd < 0) {
+ ALOGE("No kernel psi monitor support (errno=%d)", errno);
+ return -1;
+ }
+
+ switch (stall_type) {
+ case (PSI_SOME):
+ case (PSI_FULL):
+ res = snprintf(buf, sizeof(buf), "%s %d %d",
+ stall_type_name[stall_type], threshold_us, window_us);
+ break;
+ default:
+ ALOGE("Invalid psi stall type: %d", stall_type);
+ errno = EINVAL;
+ goto err;
+ }
+
+ if (res >= (ssize_t)sizeof(buf)) {
+ ALOGE("%s line overflow for psi stall type '%s'",
+ PSI_MON_FILE_MEMORY, stall_type_name[stall_type]);
+ errno = EINVAL;
+ goto err;
+ }
+
+ res = TEMP_FAILURE_RETRY(write(fd, buf, strlen(buf) + 1));
+ if (res < 0) {
+ ALOGE("%s write failed for psi stall type '%s'; errno=%d",
+ PSI_MON_FILE_MEMORY, stall_type_name[stall_type], errno);
+ goto err;
+ }
+
+ return fd;
+
+err:
+ close(fd);
+ return -1;
+}
+
+int register_psi_monitor(int epollfd, int fd, void* data) {
+ int res;
+ struct epoll_event epev;
+
+ epev.events = EPOLLPRI;
+ epev.data.ptr = data;
+ res = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epev);
+ if (res < 0) {
+ ALOGE("epoll_ctl for psi monitor failed; errno=%d", errno);
+ }
+ return res;
+}
+
+int unregister_psi_monitor(int epollfd, int fd) {
+ return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+void destroy_psi_monitor(int fd) {
+ if (fd >= 0) {
+ close(fd);
+ }
+}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index c9c9e8e..42af751 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,6 +16,7 @@
#define LOG_TAG "lowmemorykiller"
+#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <pwd.h>
@@ -28,18 +29,23 @@
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
+#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/sysinfo.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <lmkd.h>
#include <log/log.h>
#include <log/log_event_list.h>
#include <log/log_time.h>
+#include <psi/psi.h>
+#include <system/thread_defs.h>
#ifdef LMKD_LOG_STATS
#include "statslog.h"
@@ -73,6 +79,7 @@
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define MEMINFO_PATH "/proc/meminfo"
+#define PROC_STATUS_TGID_FIELD "Tgid:"
#define LINE_MAX 128
/* Android Logger event logtags (see event.logtags) */
@@ -88,6 +95,7 @@
#define TARGET_UPDATE_MIN_INTERVAL_MS 1000
#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
+#define US_PER_MS (US_PER_SEC / MS_PER_SEC)
/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
#define SYSTEM_ADJ (-900)
@@ -95,6 +103,18 @@
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
#define STRINGIFY_INTERNAL(x) #x
+/*
+ * PSI monitor tracking window size.
+ * PSI monitor generates events at most once per window,
+ * therefore we poll memory state for the duration of
+ * PSI_WINDOW_SIZE_MS after the event happens.
+ */
+#define PSI_WINDOW_SIZE_MS 1000
+/* Polling period after initial PSI signal */
+#define PSI_POLL_PERIOD_MS 10
+/* Poll for the duration of one window after initial PSI signal */
+#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
+
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define FAIL_REPORT_RLIMIT_MS 1000
@@ -122,6 +142,11 @@
int64_t max_nr_free_pages;
} low_pressure_mem = { -1, -1 };
+struct psi_threshold {
+ enum psi_stall_type stall_type;
+ int threshold_ms;
+};
+
static int level_oomadj[VMPRESS_LEVEL_COUNT];
static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
static bool debug_process_killing;
@@ -134,6 +159,12 @@
static bool use_minfree_levels;
static bool per_app_memcg;
static int swap_free_low_percentage;
+static bool use_psi_monitors = false;
+static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
+ { PSI_SOME, 70 }, /* 70ms out of 1sec for partial stall */
+ { PSI_SOME, 100 }, /* 100ms out of 1sec for partial stall */
+ { PSI_FULL, 70 }, /* 70ms out of 1sec for complete stall */
+};
static android_log_context ctx;
@@ -159,8 +190,8 @@
/* vmpressure event handler data */
static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
+#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
static int epollfd;
static int maxevents;
@@ -521,6 +552,49 @@
(to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
}
+static int proc_get_tgid(int pid) {
+ char path[PATH_MAX];
+ char buf[PAGE_SIZE];
+ int fd;
+ ssize_t size;
+ char *pos;
+ int64_t tgid = -1;
+
+ snprintf(path, PATH_MAX, "/proc/%d/status", pid);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ return -1;
+ }
+
+ size = read_all(fd, buf, sizeof(buf) - 1);
+ if (size < 0) {
+ goto out;
+ }
+ buf[size] = 0;
+
+ pos = buf;
+ while (true) {
+ pos = strstr(pos, PROC_STATUS_TGID_FIELD);
+ /* Stop if TGID tag not found or found at the line beginning */
+ if (pos == NULL || pos == buf || pos[-1] == '\n') {
+ break;
+ }
+ pos++;
+ }
+
+ if (pos == NULL) {
+ goto out;
+ }
+
+ pos += strlen(PROC_STATUS_TGID_FIELD);
+ while (*pos == ' ') pos++;
+ parse_int64(pos, &tgid);
+
+out:
+ close(fd);
+ return (int)tgid;
+}
+
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
@@ -529,6 +603,7 @@
struct lmk_procprio params;
bool is_system_server;
struct passwd *pwdrec;
+ int tgid;
lmkd_pack_get_procprio(packet, ¶ms);
@@ -538,6 +613,14 @@
return;
}
+ /* Check if registered process is a thread group leader */
+ tgid = proc_get_tgid(params.pid);
+ if (tgid >= 0 && tgid != params.pid) {
+ ALOGE("Attempt to register a task that is not a thread group leader (tid %d, tgid %d)",
+ params.pid, tgid);
+ return;
+ }
+
/* gid containing AID_READPROC required */
/* CAP_SYS_RESOURCE required */
/* CAP_DAC_OVERRIDE required */
@@ -1018,19 +1101,20 @@
// field 10 is pgfault
// field 12 is pgmajfault
+ // field 22 is starttime
// field 24 is rss_in_pages
- int64_t pgfault = 0, pgmajfault = 0, rss_in_pages = 0;
+ int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
if (sscanf(buffer,
"%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
"%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
- "%*d %*d %" SCNd64 "",
- &pgfault, &pgmajfault, &rss_in_pages) != 3) {
+ "%" SCNd64 " %*d %" SCNd64 "",
+ &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
return -1;
}
mem_st->pgfault = pgfault;
mem_st->pgmajfault = pgmajfault;
mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
-
+ mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
return 0;
}
#endif
@@ -1260,12 +1344,48 @@
return maxprocp;
}
+static void set_process_group_and_prio(int pid, SchedPolicy sp, int prio) {
+ DIR* d;
+ char proc_path[PATH_MAX];
+ struct dirent* de;
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%d/task", pid);
+ if (!(d = opendir(proc_path))) {
+ ALOGW("Failed to open %s; errno=%d: process pid(%d) might have died", proc_path, errno,
+ pid);
+ return;
+ }
+
+ while ((de = readdir(d))) {
+ int t_pid;
+
+ if (de->d_name[0] == '.') continue;
+ t_pid = atoi(de->d_name);
+
+ if (!t_pid) {
+ ALOGW("Failed to get t_pid for '%s' of pid(%d)", de->d_name, pid);
+ continue;
+ }
+
+ if (setpriority(PRIO_PROCESS, t_pid, prio) && errno != ESRCH) {
+ ALOGW("Unable to raise priority of killing t_pid (%d): errno=%d", t_pid, errno);
+ }
+
+ if (set_cpuset_policy(t_pid, sp)) {
+ ALOGW("Failed to set_cpuset_policy on pid(%d) t_pid(%d) to %d", pid, t_pid, (int)sp);
+ continue;
+ }
+ }
+ closedir(d);
+}
+
static int last_killed_pid = -1;
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
int pid = procp->pid;
uid_t uid = procp->uid;
+ int tgid;
char *taskname;
int tasksize;
int r;
@@ -1274,8 +1394,17 @@
#ifdef LMKD_LOG_STATS
struct memory_stat mem_st = {};
int memory_stat_parse_result = -1;
+#else
+ /* To prevent unused parameter warning */
+ (void)(min_oom_score);
#endif
+ tgid = proc_get_tgid(pid);
+ if (tgid >= 0 && tgid != pid) {
+ ALOGE("Possible pid reuse detected (pid %d, tgid %d)!", pid, tgid);
+ goto out;
+ }
+
taskname = proc_get_name(pid);
if (!taskname) {
goto out;
@@ -1300,6 +1429,9 @@
/* CAP_KILL required */
r = kill(pid, SIGKILL);
+
+ set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+
inc_killcnt(procp->oomadj);
ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
taskname, pid, uid, procp->oomadj, tasksize * page_k);
@@ -1316,10 +1448,12 @@
if (memory_stat_parse_result == 0) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
- mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+ mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
+ min_oom_score);
} else if (enable_stats_log) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
- -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1);
+ -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
+ min_oom_score);
}
#endif
result = tasksize;
@@ -1356,7 +1490,7 @@
if (!procp)
break;
- killed_size = kill_one_process(procp);
+ killed_size = kill_one_process(procp, min_score_adj);
if (killed_size >= 0) {
#ifdef LMKD_LOG_STATS
if (enable_stats_log && !lmk_state_change_start) {
@@ -1480,17 +1614,23 @@
.fd = -1,
};
- /*
- * Check all event counters from low to critical
- * and upgrade to the highest priority one. By reading
- * eventfd we also reset the event counters.
- */
- for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
- if (mpevfd[lvl] != -1 &&
- TEMP_FAILURE_RETRY(read(mpevfd[lvl],
- &evcount, sizeof(evcount))) > 0 &&
- evcount > 0 && lvl > level) {
- level = lvl;
+ if (debug_process_killing) {
+ ALOGI("%s memory pressure event is triggered", level_name[level]);
+ }
+
+ if (!use_psi_monitors) {
+ /*
+ * Check all event counters from low to critical
+ * and upgrade to the highest priority one. By reading
+ * eventfd we also reset the event counters.
+ */
+ for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+ if (mpevfd[lvl] != -1 &&
+ TEMP_FAILURE_RETRY(read(mpevfd[lvl],
+ &evcount, sizeof(evcount))) > 0 &&
+ evcount > 0 && lvl > level) {
+ level = lvl;
+ }
}
}
@@ -1619,16 +1759,6 @@
static unsigned long report_skip_count = 0;
if (!use_minfree_levels) {
- /* If pressure level is less than critical and enough free swap then ignore */
- if (level < VMPRESS_LEVEL_CRITICAL &&
- mi.field.free_swap > low_pressure_mem.max_nr_free_pages) {
- if (debug_process_killing) {
- ALOGI("Ignoring pressure since %" PRId64
- " swap pages are available ",
- mi.field.free_swap);
- }
- return;
- }
/* Free up enough memory to downgrate the memory pressure to low level */
if (mi.field.nr_free_pages >= low_pressure_mem.max_nr_free_pages) {
if (debug_process_killing) {
@@ -1678,6 +1808,54 @@
}
}
+static bool init_mp_psi(enum vmpressure_level level) {
+ int fd = init_psi_monitor(psi_thresholds[level].stall_type,
+ psi_thresholds[level].threshold_ms * US_PER_MS,
+ PSI_WINDOW_SIZE_MS * US_PER_MS);
+
+ if (fd < 0) {
+ return false;
+ }
+
+ vmpressure_hinfo[level].handler = mp_event_common;
+ vmpressure_hinfo[level].data = level;
+ if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
+ destroy_psi_monitor(fd);
+ return false;
+ }
+ maxevents++;
+ mpevfd[level] = fd;
+
+ return true;
+}
+
+static void destroy_mp_psi(enum vmpressure_level level) {
+ int fd = mpevfd[level];
+
+ if (unregister_psi_monitor(epollfd, fd) < 0) {
+ ALOGE("Failed to unregister psi monitor for %s memory pressure; errno=%d",
+ level_name[level], errno);
+ }
+ destroy_psi_monitor(fd);
+ mpevfd[level] = -1;
+}
+
+static bool init_psi_monitors() {
+ if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
+ return false;
+ }
+ if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
+ destroy_mp_psi(VMPRESS_LEVEL_LOW);
+ return false;
+ }
+ if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
+ destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
+ destroy_mp_psi(VMPRESS_LEVEL_LOW);
+ return false;
+ }
+ return true;
+}
+
static bool init_mp_common(enum vmpressure_level level) {
int mpfd;
int evfd;
@@ -1745,6 +1923,74 @@
return false;
}
+#ifdef LMKD_LOG_STATS
+static int kernel_poll_fd = -1;
+
+static void poll_kernel() {
+ if (kernel_poll_fd == -1) {
+ // not waiting
+ return;
+ }
+
+ while (1) {
+ char rd_buf[256];
+ int bytes_read =
+ TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+ if (bytes_read <= 0) break;
+ rd_buf[bytes_read] = '\0';
+
+ int64_t pid;
+ int64_t uid;
+ int64_t group_leader_pid;
+ int64_t min_flt;
+ int64_t maj_flt;
+ int64_t rss_in_pages;
+ int16_t oom_score_adj;
+ int16_t min_score_adj;
+ int64_t starttime;
+ char* taskname = 0;
+ int fields_read = sscanf(rd_buf,
+ "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+ " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+ &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
+ &oom_score_adj, &min_score_adj, &starttime, &taskname);
+
+ /* only the death of the group leader process is logged */
+ if (fields_read == 10 && group_leader_pid == pid) {
+ int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+ stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
+ min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+ process_start_time_ns, min_score_adj);
+ }
+
+ free(taskname);
+ }
+}
+
+static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
+
+static void init_poll_kernel() {
+ struct epoll_event epev;
+ kernel_poll_fd =
+ TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+ if (kernel_poll_fd < 0) {
+ ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
+ return;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void*)&kernel_poll_hinfo;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
+ close(kernel_poll_fd);
+ kernel_poll_fd = -1;
+ } else {
+ maxevents++;
+ }
+}
+#endif
+
static int init(void) {
struct epoll_event epev;
int i;
@@ -1792,13 +2038,28 @@
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
+#ifdef LMKD_LOG_STATS
+ if (enable_stats_log) {
+ init_poll_kernel();
+ }
+#endif
} else {
- if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+ /* Try to use psi monitor first if kernel has it */
+ use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
+ init_psi_monitors();
+ /* Fall back to vmpressure */
+ if (!use_psi_monitors &&
+ (!init_mp_common(VMPRESS_LEVEL_LOW) ||
!init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
- !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
+ !init_mp_common(VMPRESS_LEVEL_CRITICAL))) {
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
return -1;
}
+ if (use_psi_monitors) {
+ ALOGI("Using psi monitors for memory pressure detection");
+ } else {
+ ALOGI("Using vmpressure for memory pressure detection");
+ }
}
for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -1813,14 +2074,37 @@
static void mainloop(void) {
struct event_handler_info* handler_info;
+ struct event_handler_info* poll_handler = NULL;
+ struct timespec last_report_tm, curr_tm;
struct epoll_event *evt;
+ long delay = -1;
+ int polling = 0;
while (1) {
struct epoll_event events[maxevents];
int nevents;
int i;
- nevents = epoll_wait(epollfd, events, maxevents, -1);
+ if (polling) {
+ /* Calculate next timeout */
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+ delay = get_time_diff_ms(&last_report_tm, &curr_tm);
+ delay = (delay < PSI_POLL_PERIOD_MS) ?
+ PSI_POLL_PERIOD_MS - delay : PSI_POLL_PERIOD_MS;
+
+ /* Wait for events until the next polling timeout */
+ nevents = epoll_wait(epollfd, events, maxevents, delay);
+
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+ if (get_time_diff_ms(&last_report_tm, &curr_tm) >= PSI_POLL_PERIOD_MS) {
+ polling--;
+ poll_handler->handler(poll_handler->data, 0);
+ last_report_tm = curr_tm;
+ }
+ } else {
+ /* Wait for events with no timeout */
+ nevents = epoll_wait(epollfd, events, maxevents, -1);
+ }
if (nevents == -1) {
if (errno == EINTR)
@@ -1855,6 +2139,17 @@
if (evt->data.ptr) {
handler_info = (struct event_handler_info*)evt->data.ptr;
handler_info->handler(handler_info->data, evt->events);
+
+ if (use_psi_monitors && handler_info->handler == mp_event_common) {
+ /*
+ * Poll for the duration of PSI_WINDOW_SIZE_MS after the
+ * initial PSI event because psi events are rate-limited
+ * at one per sec.
+ */
+ polling = PSI_POLL_COUNT;
+ poll_handler = handler_info;
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &last_report_tm);
+ }
}
}
}
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 66d1164..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -65,7 +65,8 @@
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes) {
+ int64_t swap_in_bytes, int64_t process_start_time_ns,
+ int32_t min_oom_score) {
assert(ctx != NULL);
int ret = -EINVAL;
if (!ctx) {
@@ -113,5 +114,13 @@
return ret;
}
+ if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
+ return ret;
+ }
+
+ if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+ return ret;
+ }
+
return write_to_logger(ctx, LOG_ID_STATS);
}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index 8458480..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -64,6 +64,7 @@
int64_t rss_in_bytes;
int64_t cache_in_bytes;
int64_t swap_in_bytes;
+ int64_t process_start_time_ns;
};
#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
@@ -87,7 +88,8 @@
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes);
+ int64_t swap_in_bytes, int64_t process_start_time_ns,
+ int32_t min_oom_score);
__END_DECLS
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
index cbf44e9..4e845fd 100644
--- a/lmkd/tests/Android.bp
+++ b/lmkd/tests/Android.bp
@@ -18,6 +18,7 @@
shared_libs: [
"libbase",
"liblog",
+ "libcutils",
],
static_libs: [
diff --git a/logcat/Android.bp b/logcat/Android.bp
index b0563a6..5030b15 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -24,10 +24,10 @@
],
shared_libs: [
"libbase",
- "libcutils",
- "liblog",
"libpcrecpp",
+ "libprocessgroup",
],
+ static_libs: ["liblog"],
logtags: ["event.logtags"],
}
diff --git a/logcat/Android.mk b/logcat/Android.mk
deleted file mode 100644
index a716993..0000000
--- a/logcat/Android.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright 2006-2014 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logcat/event.logtags b/logcat/event.logtags
index da8d2d4..3a1d36f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -116,8 +116,8 @@
# audio
# 61000 - 61199 reserved for audioserver
-# 0 for screen off, 1 for screen on, 2 for key-guard done
-70000 screen_toggled (screen_state|1|5)
+# com.android.server.policy
+# 70000 - 70199 reserved for PhoneWindowManager and other policies
# aggregation service
70200 aggregation (aggregation time|2|3)
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 87bc6ae..6e38d95 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -49,11 +49,11 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <log/logprint.h>
#include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
#include <system/thread_defs.h>
#include <pcrecpp.h>
@@ -483,7 +483,8 @@
" Additionally, 'kernel' for userdebug and eng builds, and\n"
" 'security' for Device Owner installations.\n"
" Multiple -b parameters or comma separated list of buffers are\n"
- " allowed. Buffers interleaved. Default -b main,system,crash.\n"
+ " allowed. Buffers interleaved.\n"
+ " Default -b main,system,crash,kernel.\n"
" -B, --binary Output the log in binary.\n"
" -S, --statistics Output statistics.\n"
" -p, --prune Print prune white and ~black list. Service is specified as\n"
@@ -1312,6 +1313,10 @@
dev = dev->next = new log_device_t("crash", false);
context->devCount++;
}
+ if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
+ dev = dev->next = new log_device_t("kernel", false);
+ context->devCount++;
+ }
}
if (!!context->logRotateSizeKBytes && !context->outputFileName) {
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 07040b0..26c9de3 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -4,24 +4,22 @@
# Make sure any property changes are only performed with /data mounted, after
# post-fs-data state because otherwise behavior is undefined. The exceptions
# are device adjustments for logcatd service properties (persist.* overrides
-# notwithstanding) for logd.logpersistd.size and logd.logpersistd.buffer.
+# notwithstanding) for logd.logpersistd.size logd.logpersistd.rotate_kbytes and
+# logd.logpersistd.buffer.
# persist to non-persistent trampolines to permit device properties can be
# overridden when /data mounts, or during runtime.
-on property:persist.logd.logpersistd.size=256
- setprop persist.logd.logpersistd.size ""
- setprop logd.logpersistd.size ""
+on property:persist.logd.logpersistd.count=*
+ # expect /init to report failure if property empty (default)
+ setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
on property:persist.logd.logpersistd.size=*
- # expect /init to report failure if property empty (default)
setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
-on property:persist.logd.logpersistd.buffer=all
- setprop persist.logd.logpersistd.buffer ""
- setprop logd.logpersistd.buffer ""
+on property:persist.logd.logpersistd.rotate_kbytes=*
+ setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
on property:persist.logd.logpersistd.buffer=*
- # expect /init to report failure if property empty (default)
setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
on property:persist.logd.logpersistd=logcatd
@@ -54,7 +52,7 @@
stop logcatd
# logcatd service
-service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-1024} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
class late_start
disabled
# logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/tests/Android.bp b/logcat/tests/Android.bp
new file mode 100644
index 0000000..ab84150
--- /dev/null
+++ b/logcat/tests/Android.bp
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2013-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+ name: "logcat-tests-defaults",
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+ ],
+}
+
+// -----------------------------------------------------------------------------
+// Benchmarks
+// ----------------------------------------------------------------------------
+
+// Build benchmarks for the device. Run with:
+// adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
+cc_benchmark {
+ name: "logcat-benchmarks",
+ defaults: ["logcat-tests-defaults"],
+ srcs: ["logcat_benchmark.cpp"],
+ shared_libs: ["libbase"],
+}
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+// Build tests for the device (with .so). Run with:
+// adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
+cc_test {
+ name: "logcat-unit-tests",
+ defaults: ["logcat-tests-defaults"],
+ shared_libs: ["libbase"],
+ static_libs: ["liblog"],
+ srcs: [
+ "logcat_test.cpp",
+ "logcatd_test.cpp",
+ ],
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
deleted file mode 100644
index 66f6724..0000000
--- a/logcat/tests/Android.mk
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# Copyright (C) 2013-2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := logcat-
-test_tags := tests
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-# -----------------------------------------------------------------------------
-# Benchmarks
-# ----------------------------------------------------------------------------
-
-benchmark_src_files := \
- logcat_benchmark.cpp \
-
-# Build benchmarks for the device. Run with:
-# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)benchmarks
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(benchmark_src_files)
-LOCAL_SHARED_LIBRARIES := libbase
-include $(BUILD_NATIVE_BENCHMARK)
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_src_files := \
- logcat_test.cpp \
- logcatd_test.cpp \
-
-# Build tests for the device (with .so). Run with:
-# adb shell /data/nativetest/logcat-unit-tests/logcat-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libbase
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 9483bb2..b32b437 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -33,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <gtest/gtest.h>
#include <log/event_tag_map.h>
#include <log/log.h>
@@ -1404,7 +1405,7 @@
int count = 0;
char buffer[BIG_BUFFER];
-#define logcat_regex_prefix ___STRING(logcat) "_test"
+#define logcat_regex_prefix logcat_executable "_test"
snprintf(buffer, sizeof(buffer),
logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
@@ -1550,7 +1551,7 @@
{
static const struct tag sync = { 2720, "sync" };
- static const char id[] = ___STRING(logcat) ".descriptive-sync";
+ static const char id[] = logcat_executable ".descriptive-sync";
{
android_log_event_list ctx(sync.tagNo);
ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
@@ -1665,7 +1666,7 @@
// Invent new entries because existing can not serve
EventTagMap* map = android_openEventTagMap(nullptr);
ASSERT_TRUE(nullptr != map);
- static const char name[] = ___STRING(logcat) ".descriptive-monotonic";
+ static const char name[] = logcat_executable ".descriptive-monotonic";
int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
ANDROID_LOG_UNKNOWN);
android_closeEventTagMap(map);
@@ -1747,3 +1748,13 @@
EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
#endif
}
+
+TEST(logcat, invalid_buffer) {
+ FILE* fp = popen("logcat -b foo 2>&1", "r");
+ ASSERT_NE(nullptr, fp);
+ std::string output;
+ ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
+ pclose(fp);
+
+ ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+}
diff --git a/logd/.clang-format b/logd/.clang-format
deleted file mode 100644
index 393c309..0000000
--- a/logd/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: false
-
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-PenaltyExcessCharacter: 32
-
-Cpp11BracedListStyle: false
diff --git a/logd/.clang-format b/logd/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/logd/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/logd/Android.bp b/logd/Android.bp
index 5c79976..b337b7c 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -39,7 +39,6 @@
"FlushCommand.cpp",
"LogBuffer.cpp",
"LogBufferElement.cpp",
- "LogBufferInterface.cpp",
"LogTimes.cpp",
"LogStatistics.cpp",
"LogWhiteBlackList.cpp",
@@ -63,16 +62,44 @@
srcs: ["main.cpp"],
- static_libs: ["liblogd"],
+ static_libs: [
+ "liblog",
+ "liblogd",
+ ],
shared_libs: [
"libsysutils",
- "liblog",
"libcutils",
"libbase",
"libpackagelistparser",
+ "libprocessgroup",
"libcap",
],
cflags: ["-Werror"],
}
+
+cc_binary {
+ name: "auditctl",
+
+ srcs: ["auditctl.cpp"],
+
+ static_libs: [
+ "liblogd",
+ ],
+
+ shared_libs: ["libbase"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wconversion"
+ ],
+}
+
+prebuilt_etc {
+ name: "logtagd.rc",
+ src: "logtagd.rc",
+ sub_dir: "init",
+}
diff --git a/logd/Android.mk b/logd/Android.mk
deleted file mode 100644
index 1bca891..0000000
--- a/logd/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logtagd.rc
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-
-include $(BUILD_PREBUILT)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index fc8c960..5a375ec 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -111,7 +111,7 @@
}
std::map<std::string, std::string> LogAudit::populateDenialMap() {
- std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
+ std::ifstream bug_file("/vendor/etc/selinux/selinux_denial_metadata");
std::string line;
// allocate a map for the static map pointer in auditParse to keep track of,
// this function only runs once
@@ -165,9 +165,14 @@
bug_num->assign("");
}
+ // Ensure the uid name is not null before passing it to the bug string.
if (uid >= AID_APP_START && uid <= AID_APP_END) {
- bug_num->append(" app=");
- bug_num->append(android::uidToName(uid));
+ char* uidname = android::uidToName(uid);
+ if (uidname) {
+ bug_num->append(" app=");
+ bug_num->append(uidname);
+ free(uidname);
+ }
}
}
@@ -224,70 +229,17 @@
static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
static const char newline[] = "\n";
- // Dedupe messages, checking for identical messages starting with avc:
- static unsigned count;
- static char* last_str;
- static bool last_info;
+ auditParse(str, uid, &denial_metadata);
+ iov[0].iov_base = info ? const_cast<char*>(log_info) : const_cast<char*>(log_warning);
+ iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+ iov[1].iov_base = str;
+ iov[1].iov_len = strlen(str);
+ iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+ iov[2].iov_len = denial_metadata.length();
+ iov[3].iov_base = const_cast<char*>(newline);
+ iov[3].iov_len = strlen(newline);
- if (last_str != nullptr) {
- static const char avc[] = "): avc: ";
- char* avcl = strstr(last_str, avc);
- bool skip = false;
-
- if (avcl) {
- char* avcr = strstr(str, avc);
-
- skip = avcr &&
- !fastcmp<strcmp>(avcl + strlen(avc), avcr + strlen(avc));
- if (skip) {
- ++count;
- free(last_str);
- last_str = strdup(str);
- last_info = info;
- }
- }
- if (!skip) {
- static const char resume[] = " duplicate messages suppressed\n";
- iov[0].iov_base = last_info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len =
- last_info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = last_str;
- iov[1].iov_len = strlen(last_str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- if (count > 1) {
- iov[3].iov_base = const_cast<char*>(resume);
- iov[3].iov_len = strlen(resume);
- } else {
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
- }
-
- writev(fdDmesg, iov, arraysize(iov));
- free(last_str);
- last_str = nullptr;
- }
- }
- if (last_str == nullptr) {
- count = 0;
- last_str = strdup(str);
- last_info = info;
- }
- if (count == 0) {
- auditParse(str, uid, &denial_metadata);
- iov[0].iov_base = info ? const_cast<char*>(log_info)
- : const_cast<char*>(log_warning);
- iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
- iov[1].iov_base = str;
- iov[1].iov_len = strlen(str);
- iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
- iov[2].iov_len = denial_metadata.length();
- iov[3].iov_base = const_cast<char*>(newline);
- iov[3].iov_len = strlen(newline);
-
- writev(fdDmesg, iov, arraysize(iov));
- }
+ writev(fdDmesg, iov, arraysize(iov));
}
if (!main && !events) {
@@ -295,7 +247,7 @@
return 0;
}
- log_time now;
+ log_time now(log_time::EPOCH);
static const char audit_str[] = " audit(";
char* timeptr = strstr(str, audit_str);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fbdbf79..9cbc7c4 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -399,7 +399,7 @@
((*it)->getLogId() != LOG_ID_KERNEL))) {
mLogElements.push_back(elem);
} else {
- log_time end = log_time::EPOCH;
+ log_time end(log_time::EPOCH);
bool end_set = false;
bool end_always = false;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 774d4ab..c2d5b97 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,7 +27,6 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
-#include "LogBufferInterface.h"
#include "LogStatistics.h"
#include "LogTags.h"
#include "LogTimes.h"
@@ -75,7 +74,7 @@
typedef std::list<LogBufferElement*> LogBufferElementCollection;
-class LogBuffer : public LogBufferInterface {
+class LogBuffer {
LogBufferElementCollection mLogElements;
pthread_rwlock_t mLogElementsLock;
@@ -108,14 +107,14 @@
LastLogTimes& mTimes;
explicit LogBuffer(LastLogTimes* times);
- ~LogBuffer() override;
+ ~LogBuffer();
void init();
bool isMonotonic() {
return monotonic;
}
- int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
- const char* msg, uint16_t len) override;
+ int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+ uint16_t len);
// lastTid is an optional context to help detect if the last previous
// valid message was from the same source so we can differentiate chatty
// filter types (identical or expired)
@@ -159,12 +158,7 @@
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
}
- virtual uid_t pidToUid(pid_t pid) override {
- return stats.pidToUid(pid);
- }
- virtual pid_t tidToPid(pid_t tid) override {
- return stats.tidToPid(tid);
- }
+ uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
const char* uidToName(uid_t uid) {
return stats.uidToName(uid);
}
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 19e6d68..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -55,8 +55,19 @@
mMsgLen(elem.mMsgLen),
mLogId(elem.mLogId),
mDropped(elem.mDropped) {
- mMsg = new char[mMsgLen];
- memcpy(mMsg, elem.mMsg, mMsgLen);
+ if (mDropped) {
+ if (elem.isBinary() && elem.mMsg != nullptr) {
+ // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
+ const int len = sizeof(android_event_header_t);
+ mMsg = new char[len];
+ memcpy(mMsg, elem.mMsg, len);
+ } else {
+ mMsg = nullptr;
+ }
+ } else {
+ mMsg = new char[mMsgLen];
+ memcpy(mMsg, elem.mMsg, mMsgLen);
+ }
}
LogBufferElement::~LogBufferElement() {
diff --git a/logd/LogBufferInterface.cpp b/logd/LogBufferInterface.cpp
deleted file mode 100644
index 4b6d363..0000000
--- a/logd/LogBufferInterface.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LogBufferInterface.h"
-#include "LogUtils.h"
-
-LogBufferInterface::LogBufferInterface() {
-}
-LogBufferInterface::~LogBufferInterface() {
-}
-uid_t LogBufferInterface::pidToUid(pid_t pid) {
- return android::pidToUid(pid);
-}
-pid_t LogBufferInterface::tidToPid(pid_t tid) {
- return android::tidToPid(tid);
-}
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
deleted file mode 100644
index f31e244..0000000
--- a/logd/LogBufferInterface.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LOGD_LOG_BUFFER_INTERFACE_H__
-#define _LOGD_LOG_BUFFER_INTERFACE_H__
-
-#include <sys/types.h>
-
-#include <android-base/macros.h>
-#include <log/log_id.h>
-#include <log/log_time.h>
-
-// Abstract interface that handles log when log available.
-class LogBufferInterface {
- public:
- LogBufferInterface();
- virtual ~LogBufferInterface();
- // Handles a log entry when available in LogListener.
- // Returns the size of the handled log message.
- virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
- pid_t tid, const char* msg, uint16_t len) = 0;
-
- virtual uid_t pidToUid(pid_t pid);
- virtual pid_t tidToPid(pid_t tid);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
-};
-
-#endif // _LOGD_LOG_BUFFER_INTERFACE_H__
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 513c0c3..edd326a 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -197,10 +197,9 @@
// NOTREACHED
}
-log_time LogKlog::correction =
- (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
- ? log_time::EPOCH
- : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
+log_time LogKlog::correction = (log_time(CLOCK_REALTIME) < log_time(CLOCK_MONOTONIC))
+ ? log_time(log_time::EPOCH)
+ : (log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC));
LogKlog::LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
bool auditd)
@@ -268,7 +267,7 @@
static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
if (len < (ssize_t)(strlen(real_format) + 5)) return;
- log_time real;
+ log_time real(log_time::EPOCH);
const char* ep = real.strptime(real_string, real_format);
if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
return;
@@ -282,20 +281,20 @@
tm.tm_isdst = -1;
localtime_r(&now, &tm);
if ((tm.tm_gmtoff < 0) && ((-tm.tm_gmtoff) > (long)real.tv_sec)) {
- real = log_time::EPOCH;
+ real = log_time(log_time::EPOCH);
} else {
real.tv_sec += tm.tm_gmtoff;
}
if (monotonic > real) {
- correction = log_time::EPOCH;
+ correction = log_time(log_time::EPOCH);
} else {
correction = real - monotonic;
}
}
-void LogKlog::sniffTime(log_time& now, const char*& buf, ssize_t len,
- bool reverse) {
- if (len <= 0) return;
+log_time LogKlog::sniffTime(const char*& buf, ssize_t len, bool reverse) {
+ log_time now(log_time::EPOCH);
+ if (len <= 0) return now;
const char* cp = nullptr;
if ((len > 10) && (*buf == '[')) {
@@ -310,7 +309,7 @@
}
buf = cp;
- if (isMonotonic()) return;
+ if (isMonotonic()) return now;
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
@@ -329,11 +328,11 @@
// trigger a check for ntp-induced or hardware clock drift.
log_time real(CLOCK_REALTIME);
log_time mono(CLOCK_MONOTONIC);
- correction = (real < mono) ? log_time::EPOCH : (real - mono);
+ correction = (real < mono) ? log_time(log_time::EPOCH) : (real - mono);
} else if (((b = android::strnstr(cp, len, suspendedStr))) &&
(((b += strlen(suspendStr)) - cp) < len)) {
len -= b - cp;
- log_time real;
+ log_time real(log_time::EPOCH);
char* endp;
real.tv_sec = strtol(b, &endp, 10);
if ((*endp == '.') && ((endp - b) < len)) {
@@ -345,7 +344,7 @@
}
if (reverse) {
if (real > correction) {
- correction = log_time::EPOCH;
+ correction = log_time(log_time::EPOCH);
} else {
correction -= real;
}
@@ -363,6 +362,7 @@
now = log_time(CLOCK_REALTIME);
}
}
+ return now;
}
pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
@@ -451,8 +451,7 @@
}
parseKernelPrio(cp, len - (cp - buf));
- log_time now;
- sniffTime(now, cp, len - (cp - buf), true);
+ log_time now = sniffTime(cp, len - (cp - buf), true);
const char* suspended = android::strnstr(buf, len, suspendedStr);
if (!suspended || (suspended > cp)) {
@@ -468,7 +467,7 @@
}
parseKernelPrio(cp, len - (cp - buf));
- sniffTime(now, cp, len - (cp - buf), true);
+ sniffTime(cp, len - (cp - buf), true);
}
// Convert kernel log priority number into an Android Logger priority number
@@ -548,8 +547,7 @@
const char* p = buf;
int pri = parseKernelPrio(p, len);
- log_time now;
- sniffTime(now, p, len - (p - buf), false);
+ log_time now = sniffTime(p, len - (p - buf), false);
// sniff for start marker
const char* start = android::strnstr(p, len - (p - buf), klogdStr);
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index bb92dd2..6bfd6a8 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -55,11 +55,10 @@
}
protected:
- void sniffTime(log_time& now, const char*& buf, ssize_t len, bool reverse);
- pid_t sniffPid(const char*& buf, ssize_t len);
- void calculateCorrection(const log_time& monotonic, const char* real_string,
- ssize_t len);
- virtual bool onDataAvailable(SocketClient* cli);
+ log_time sniffTime(const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
+ void calculateCorrection(const log_time& monotonic, const char* real_string, ssize_t len);
+ virtual bool onDataAvailable(SocketClient* cli);
};
#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 2f22778..443570f 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#include <ctype.h>
#include <limits.h>
-#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -32,9 +30,8 @@
#include "LogListener.h"
#include "LogUtils.h"
-LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
- : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
-}
+LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+ : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
bool LogListener::onDataAvailable(SocketClient* cli) {
static bool name_set;
@@ -78,11 +75,8 @@
cmsg = CMSG_NXTHDR(&hdr, cmsg);
}
- struct ucred fake_cred;
if (cred == nullptr) {
- cred = &fake_cred;
- cred->pid = 0;
- cred->uid = DEFAULT_OVERFLOWUID;
+ return false;
}
if (cred->uid == AID_LOGD) {
@@ -106,40 +100,16 @@
return false;
}
- // Check credential validity, acquire corrected details if not supplied.
- if (cred->pid == 0) {
- cred->pid = logbuf ? logbuf->tidToPid(header->tid)
- : android::tidToPid(header->tid);
- if (cred->pid == getpid()) {
- // We expect that /proc/<tid>/ is accessible to self even without
- // readproc group, so that we will always drop messages that come
- // from any of our logd threads and their library calls.
- return false; // ignore self
- }
- }
- if (cred->uid == DEFAULT_OVERFLOWUID) {
- uid_t uid =
- logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
- if (uid == AID_LOGD) {
- uid = logbuf ? logbuf->pidToUid(header->tid)
- : android::pidToUid(cred->pid);
- }
- if (uid != AID_LOGD) cred->uid = uid;
- }
-
char* msg = ((char*)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
// NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
// truncated message to the logs.
- if (logbuf != nullptr) {
- int res = logbuf->log(
- logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
- ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
- if (res > 0 && reader != nullptr) {
- reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
- }
+ int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+ ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
+ if (res > 0) {
+ reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
}
return true;
diff --git a/logd/LogListener.h b/logd/LogListener.h
index a562a54..8fe3da4 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,22 +20,12 @@
#include <sysutils/SocketListener.h>
#include "LogReader.h"
-// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
-// the uapi headers for userspace to use. This value is filled in on the
-// out-of-band socket credentials if the OS fails to find one available.
-// One of the causes of this is if SO_PASSCRED is set, all the packets before
-// that point will have this value. We also use it in a fake credential if
-// no socket credentials are supplied.
-#ifndef DEFAULT_OVERFLOWUID
-#define DEFAULT_OVERFLOWUID 65534
-#endif
-
class LogListener : public SocketListener {
- LogBufferInterface* logbuf;
+ LogBuffer* logbuf;
LogReader* reader;
public:
- LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
+ LogListener(LogBuffer* buf, LogReader* reader);
protected:
virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 116e08e..431b778 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -837,35 +837,12 @@
}
return AID_LOGD; // associate this with the logger
}
-
-pid_t tidToPid(pid_t tid) {
- char buffer[512];
- snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
- FILE* fp = fopen(buffer, "r");
- if (fp) {
- while (fgets(buffer, sizeof(buffer), fp)) {
- int pid = tid;
- char space = 0;
- if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
- isspace(space)) {
- fclose(fp);
- return pid;
- }
- }
- fclose(fp);
- }
- return tid;
-}
}
uid_t LogStatistics::pidToUid(pid_t pid) {
return pidTable.add(pid)->second.getUid();
}
-pid_t LogStatistics::tidToPid(pid_t tid) {
- return tidTable.add(tid)->second.getPid();
-}
-
// caller must free character string
const char* LogStatistics::pidToName(pid_t pid) const {
// An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 469f6dc..0782de3 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -306,6 +306,10 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
+
struct PidEntry : public EntryBaseDropped {
const pid_t pid;
uid_t uid;
@@ -385,13 +389,6 @@
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
- TidEntry(pid_t tid)
- : EntryBaseDropped(),
- tid(tid),
- pid(android::tidToPid(tid)),
- uid(android::pidToUid(tid)),
- name(android::tidToName(tid)) {
- }
explicit TidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tid(element->getTid()),
@@ -787,7 +784,6 @@
// helper (must be locked directly or implicitly by mLogElementsLock)
const char* pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
- pid_t tidToPid(pid_t tid);
const char* uidToName(uid_t uid) const;
};
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1ab9dd1..f19e7b0 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -30,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <log/log_event_list.h>
#include <log/log_properties.h>
@@ -38,6 +39,8 @@
#include "LogTags.h"
#include "LogUtils.h"
+using android::base::make_scope_guard;
+
static LogTags* logtags;
const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
@@ -316,27 +319,29 @@
std::string Format;
android_log_list_element elem;
{
- android_log_event_list ctx(log_msg);
- elem = ctx.read();
+ auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ elem = android_log_read_next(ctx);
if (elem.type != EVENT_TYPE_LIST) {
continue;
}
- elem = ctx.read();
+ elem = android_log_read_next(ctx);
if (elem.type != EVENT_TYPE_INT) {
continue;
}
Tag = elem.data.int32;
- elem = ctx.read();
+ elem = android_log_read_next(ctx);
if (elem.type != EVENT_TYPE_STRING) {
continue;
}
Name = std::string(elem.data.string, elem.len);
- elem = ctx.read();
+ elem = android_log_read_next(ctx);
if (elem.type != EVENT_TYPE_STRING) {
continue;
}
Format = std::string(elem.data.string, elem.len);
- elem = ctx.read();
+ elem = android_log_read_next(ctx);
}
if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
@@ -524,10 +529,22 @@
tag2format_const_iterator iform = tag2format.find(tag);
std::string Format = (iform != tag2format.end()) ? iform->second : "";
- __android_log_event_list ctx(TAG_DEF_LOG_TAG);
- ctx << tag << Name << Format;
- std::string buffer(ctx);
- if (buffer.length() <= 0) return; // unlikely
+ auto ctx = create_android_logger(TAG_DEF_LOG_TAG);
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) ||
+ android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 ||
+ android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) {
+ return;
+ }
+
+ const char* cp = nullptr;
+ ssize_t len = android_log_write_list_buffer(ctx, &cp);
+
+ if (len <= 0 || cp == nullptr) {
+ return;
+ }
+
+ std::string buffer(cp, len);
/*
* struct {
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 1715501..208d67f 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -204,6 +204,10 @@
goto skip;
}
+ if (me->mRelease) {
+ goto stop;
+ }
+
if (!me->mTail) {
goto ok;
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index f4e165f..9f6f203 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -18,6 +18,7 @@
#define _LOGD_LOG_TIMES_H__
#include <pthread.h>
+#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
@@ -82,6 +83,8 @@
void cleanSkip_Locked(void);
void release_Locked(void) {
+ // gracefully shut down the socket.
+ shutdown(mClient->getSocket(), SHUT_RDWR);
mRelease = true;
pthread_cond_signal(&threadTriggeredCondition);
}
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 4dcd3e7..fa9f398 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,8 +38,6 @@
// Caller must own and free returned value
char* pidToName(pid_t pid);
char* tidToName(pid_t tid);
-uid_t pidToUid(pid_t pid);
-pid_t tidToPid(pid_t tid);
// Furnished in LogTags.cpp. Thread safe.
const char* tagToName(uint32_t tag);
diff --git a/logd/README.property b/logd/README.property
index da5f96f..d2a2cbb 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -17,10 +17,13 @@
Responds to logcatd, clear and stop.
logd.logpersistd.buffer persist logpersistd buffers to collect
logd.logpersistd.size persist logpersistd size in MB
+logd.logpersistd.rotate_kbytes persist logpersistd outout file size in KB.
persist.logd.logpersistd string Enable logpersist daemon, "logcatd"
turns on logcat -f in logd context.
persist.logd.logpersistd.buffer all logpersistd buffers to collect
persist.logd.logpersistd.size 256 logpersistd size in MB
+persist.logd.logpersistd.count 256 sets max number of rotated logs to <count>.
+persist.logd.logpersistd.rotate_kbytes 1024 logpersistd output file size in KB
persist.logd.size number ro Global default size of the buffer for
all log ids at initial startup, at
runtime use: logcat -b all -G <value>
diff --git a/logd/auditctl.cpp b/logd/auditctl.cpp
new file mode 100644
index 0000000..98bb02d
--- /dev/null
+++ b/logd/auditctl.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/parseint.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "libaudit.h"
+
+static void usage(const char* cmdline) {
+ fprintf(stderr, "Usage: %s [-r rate]\n", cmdline);
+}
+
+static void do_update_rate(uint32_t rate) {
+ int fd = audit_open();
+ if (fd == -1) {
+ error(EXIT_FAILURE, errno, "Unable to open audit socket");
+ }
+ int result = audit_rate_limit(fd, rate);
+ close(fd);
+ if (result < 0) {
+ fprintf(stderr, "Can't update audit rate limit: %d\n", result);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char* argv[]) {
+ uint32_t rate = 0;
+ bool update_rate = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "r:")) != -1) {
+ switch (opt) {
+ case 'r':
+ if (!android::base::ParseUint<uint32_t>(optarg, &rate)) {
+ error(EXIT_FAILURE, errno, "Invalid Rate");
+ }
+ update_rate = true;
+ break;
+ default: /* '?' */
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // In the future, we may add other options to auditctl
+ // so this if statement will expand.
+ // if (!update_rate && !update_backlog && !update_whatever) ...
+ if (!update_rate) {
+ fprintf(stderr, "Nothing to do\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (update_rate) {
+ do_update_rate(rate);
+ }
+
+ return 0;
+}
diff --git a/logd/libaudit.c b/logd/libaudit.c
index 9d9a857..f452c71 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -160,8 +160,7 @@
* and the the mask set to AUDIT_STATUS_PID
*/
status.pid = pid;
- status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
- status.rate_limit = AUDIT_RATE_LIMIT; /* audit entries per second */
+ status.mask = AUDIT_STATUS_PID;
/* Let the kernel know this pid will be registering for audit events */
rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -188,6 +187,14 @@
return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
}
+int audit_rate_limit(int fd, uint32_t limit) {
+ struct audit_status status;
+ memset(&status, 0, sizeof(status));
+ status.mask = AUDIT_STATUS_RATE_LIMIT;
+ status.rate_limit = limit; /* audit entries per second */
+ return audit_send(fd, AUDIT_SET, &status, sizeof(status));
+}
+
int audit_get_reply(int fd, struct audit_message* rep, reply_t block, int peek) {
ssize_t len;
int flags;
diff --git a/logd/libaudit.h b/logd/libaudit.h
index 2a93ea3..b4a92a8 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -89,8 +89,17 @@
*/
extern int audit_setup(int fd, pid_t pid);
-/* Max audit messages per second */
-#define AUDIT_RATE_LIMIT 5
+/**
+ * Throttle kernel messages at the provided rate
+ * @param fd
+ * The fd returned by a call to audit_open()
+ * @param rate
+ * The rate, in messages per second, above which the kernel
+ * should drop audit messages.
+ * @return
+ * This function returns 0 on success, -errno on error.
+ */
+extern int audit_rate_limit(int fd, uint32_t limit);
__END_DECLS
diff --git a/logd/logd.rc b/logd/logd.rc
index c740ecf..530f342 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,7 +6,8 @@
file /dev/kmsg w
user logd
group logd system package_info readproc
- capabilities SYSLOG AUDIT_CONTROL SETGID
+ capabilities SYSLOG AUDIT_CONTROL
+ priority 10
writepid /dev/cpuset/system-background/tasks
service logd-reinit /system/bin/logd --reinit
@@ -16,8 +17,19 @@
group logd
writepid /dev/cpuset/system-background/tasks
+# Limit SELinux denial generation to 5/second
+service logd-auditctl /system/bin/auditctl -r 5
+ oneshot
+ disabled
+ user logd
+ group logd
+ capabilities AUDIT_CONTROL
+
on fs
write /dev/event-log-tags "# content owned by logd
"
chown logd logd /dev/event-log-tags
chmod 0644 /dev/event-log-tags
+
+on property:sys.boot_completed=1
+ start logd-auditctl
diff --git a/logd/main.cpp b/logd/main.cpp
index 8c38d9a..23bbf86 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -17,6 +17,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/capability.h>
#include <poll.h>
#include <sched.h>
#include <semaphore.h>
@@ -38,12 +39,12 @@
#include <android-base/macros.h>
#include <cutils/android_get_control_file.h>
#include <cutils/properties.h>
-#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <packagelistparser/packagelistparser.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include <processgroup/sched_policy.h>
#include <utils/threads.h>
#include "CommandListener.h"
@@ -57,35 +58,10 @@
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, '>'
-//
-// The service is designed to be run by init, it does not respond well
-// to starting up manually. When starting up manually the sockets will
-// fail to open typically for one of the following reasons:
-// EADDRINUSE if logger is running.
-// EACCESS if started without precautions (below)
-//
-// Here is a cookbook procedure for starting up logd manually assuming
-// init is out of the way, pedantically all permissions and SELinux
-// security is put back in place:
-//
-// setenforce 0
-// rm /dev/socket/logd*
-// chmod 777 /dev/socket
-// # here is where you would attach the debugger or valgrind for example
-// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
-// sleep 1
-// chmod 755 /dev/socket
-// chown logd.logd /dev/socket/logd*
-// restorecon /dev/socket/logd*
-// setenforce 1
-//
-// If minimalism prevails, typical for debugging and security is not a concern:
-//
-// setenforce 0
-// chmod 777 /dev/socket
-// logd
-//
-
+// The service is designed to be run by init, it does not respond well to starting up manually. Init
+// has a 'sigstop' feature that sends SIGSTOP to a service immediately before calling exec(). This
+// allows debuggers, etc to be attached to logd at the very beginning, while still having init
+// handle the user, groups, capabilities, files, etc setup.
static int drop_privs(bool klogd, bool auditd) {
sched_param param = {};
@@ -99,11 +75,6 @@
return -1;
}
- if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
- android::prdebug("failed to set background cgroup");
- return -1;
- }
-
if (!__android_logger_property_get_bool("ro.debuggable",
BOOL_DEFAULT_FALSE) &&
prctl(PR_SET_DUMPABLE, 0) == -1) {
@@ -111,52 +82,26 @@
return -1;
}
- std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(),
- cap_free);
- if (cap_clear(caps.get()) < 0) return -1;
- cap_value_t cap_value[] = { CAP_SETGID, // must be first for below
- klogd ? CAP_SYSLOG : CAP_SETGID,
- auditd ? CAP_AUDIT_CONTROL : CAP_SETGID };
- if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(cap_value), cap_value,
- CAP_SET) < 0) {
+ std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
+ if (cap_clear(caps.get()) < 0) {
return -1;
}
- if (cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(cap_value), cap_value,
- CAP_SET) < 0) {
+ std::vector<cap_value_t> cap_value;
+ if (klogd) {
+ cap_value.emplace_back(CAP_SYSLOG);
+ }
+ if (auditd) {
+ cap_value.emplace_back(CAP_AUDIT_CONTROL);
+ }
+
+ if (cap_set_flag(caps.get(), CAP_PERMITTED, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
+ return -1;
+ }
+ if (cap_set_flag(caps.get(), CAP_EFFECTIVE, cap_value.size(), cap_value.data(), CAP_SET) < 0) {
return -1;
}
if (cap_set_proc(caps.get()) < 0) {
- android::prdebug(
- "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
- errno);
- return -1;
- }
-
- gid_t groups[] = { AID_READPROC };
-
- if (setgroups(arraysize(groups), groups) == -1) {
- android::prdebug("failed to set AID_READPROC groups");
- return -1;
- }
-
- if (setgid(AID_LOGD) != 0) {
- android::prdebug("failed to set AID_LOGD gid");
- return -1;
- }
-
- if (setuid(AID_LOGD) != 0) {
- android::prdebug("failed to set AID_LOGD uid");
- return -1;
- }
-
- if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
- return -1;
- }
- if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) {
- return -1;
- }
- if (cap_set_proc(caps.get()) < 0) {
- android::prdebug("failed to clear CAP_SETGID (%d)", errno);
+ android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
return -1;
}
@@ -205,67 +150,14 @@
}
}
-static sem_t uidName;
-static uid_t uid;
-static char* name;
-
static sem_t reinit;
static bool reinit_running = false;
static LogBuffer* logBuf = nullptr;
-static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
- bool rc = true;
- if (info->uid == uid) {
- name = strdup(info->name);
- // false to stop processing
- rc = false;
- }
-
- packagelist_free(info);
- return rc;
-}
-
static void* reinit_thread_start(void* /*obj*/) {
prctl(PR_SET_NAME, "logd.daemon");
- set_sched_policy(0, SP_BACKGROUND);
- setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
-
- // We should drop to AID_LOGD, if we are anything else, we have
- // even lesser privileges and accept our fate.
- gid_t groups[] = {
- AID_SYSTEM, // search access to /data/system path
- AID_PACKAGE_INFO, // readonly access to /data/system/packages.list
- };
- if (setgroups(arraysize(groups), groups) == -1) {
- android::prdebug(
- "logd.daemon: failed to set AID_SYSTEM AID_PACKAGE_INFO groups");
- }
- if (setgid(AID_LOGD) != 0) {
- android::prdebug("logd.daemon: failed to set AID_LOGD gid");
- }
- if (setuid(AID_LOGD) != 0) {
- android::prdebug("logd.daemon: failed to set AID_LOGD uid");
- }
-
- cap_t caps = cap_init();
- (void)cap_clear(caps);
- (void)cap_set_proc(caps);
- (void)cap_free(caps);
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
- // uidToName Privileged Worker
- if (uid) {
- name = nullptr;
-
- // if we got the perms wrong above, this would spam if we reported
- // problems with acquisition of an uid name from the packages.
- (void)packagelist_parse(package_list_parser_cb, nullptr);
-
- uid = 0;
- sem_post(&uidName);
- continue;
- }
-
if (fdDmesg >= 0) {
static const char reinit_message[] = { KMSG_PRIORITY(LOG_INFO),
'l',
@@ -302,26 +194,30 @@
return nullptr;
}
-static sem_t sem_name;
-
char* android::uidToName(uid_t u) {
- if (!u || !reinit_running) {
- return nullptr;
- }
+ struct Userdata {
+ uid_t uid;
+ char* name;
+ } userdata = {
+ .uid = u,
+ .name = nullptr,
+ };
- sem_wait(&sem_name);
+ packagelist_parse(
+ [](pkg_info* info, void* callback_parameter) {
+ auto userdata = reinterpret_cast<Userdata*>(callback_parameter);
+ bool result = true;
+ if (info->uid == userdata->uid) {
+ userdata->name = strdup(info->name);
+ // false to stop processing
+ result = false;
+ }
+ packagelist_free(info);
+ return result;
+ },
+ &userdata);
- // Not multi-thread safe, we use sem_name to protect
- uid = u;
-
- name = nullptr;
- sem_post(&reinit);
- sem_wait(&uidName);
- char* ret = name;
-
- sem_post(&sem_name);
-
- return ret;
+ return userdata.name;
}
// Serves as a global method to trigger reinitialization
@@ -373,11 +269,6 @@
}
static int issueReinit() {
- cap_t caps = cap_init();
- (void)cap_clear(caps);
- (void)cap_set_proc(caps);
- (void)cap_free(caps);
-
int sock = TEMP_FAILURE_RETRY(socket_local_client(
"logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
if (sock < 0) return -errno;
@@ -440,10 +331,13 @@
if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
+ bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ if (drop_privs(klogd, auditd) != 0) {
+ return EXIT_FAILURE;
+ }
+
// Reinit Thread
sem_init(&reinit, 0, 0);
- sem_init(&uidName, 0, 0);
- sem_init(&sem_name, 0, 1);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
@@ -461,12 +355,6 @@
pthread_attr_destroy(&attr);
}
- bool auditd =
- __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
- if (drop_privs(klogd, auditd) != 0) {
- return EXIT_FAILURE;
- }
-
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
new file mode 100644
index 0000000..d39da8a
--- /dev/null
+++ b/logd/tests/Android.bp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// -----------------------------------------------------------------------------
+// Unit tests.
+// -----------------------------------------------------------------------------
+
+cc_defaults {
+ name: "logd-unit-test-defaults",
+
+ cflags: [
+ "-fstack-protector-all",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-fno-builtin",
+
+ "-DAUDITD_LOG_TAG=1003",
+ "-DCHATTY_LOG_TAG=1004",
+ ],
+
+ srcs: ["logd_test.cpp"],
+
+ static_libs: [
+ "libbase",
+ "libcutils",
+ "libselinux",
+ "liblog",
+ ],
+}
+
+// Build tests for the logger. Run with:
+// adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
+cc_test {
+ name: "logd-unit-tests",
+ defaults: ["logd-unit-test-defaults"],
+}
+
+cc_test {
+ name: "CtsLogdTestCases",
+ defaults: ["logd-unit-test-defaults"],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+}
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
deleted file mode 100644
index a0875ea..0000000
--- a/logd/tests/Android.mk
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# -----------------------------------------------------------------------------
-# Benchmarks. (see ../../liblog/tests)
-# -----------------------------------------------------------------------------
-
-test_module_prefix := logd-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
- $(event_flag)
-
-test_src_files := \
- logd_test.cpp
-
-# Build tests for the logger. Run with:
-# adb shell /data/nativetest/logd-unit-tests/logd-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
-
-cts_executable := CtsLogdTestCases
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
-LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.core.logd
-include $(BUILD_CTS_EXECUTABLE)
-
-ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(cts_executable)_list
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := $(test_c_flags) -DHOST
-LOCAL_C_INCLUDES := external/gtest/include
-LOCAL_SRC_FILES := $(test_src_files)
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_CXX_STL := libc++
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_HOST_NATIVE_TEST)
-
-endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/logd/tests/AndroidTest.xml b/logd/tests/AndroidTest.xml
index 84f0764..9a18edb 100644
--- a/logd/tests/AndroidTest.xml
+++ b/logd/tests/AndroidTest.xml
@@ -16,6 +16,8 @@
<configuration description="Config for CTS Logging Daemon test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsLogdTestCases->/data/local/tmp/CtsLogdTestCases" />
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 7d7a22f..b6c33d7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,7 +39,6 @@
#endif
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
-#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -953,7 +952,7 @@
void __android_log_btwrite_multiple__helper(int count) {
#ifdef __ANDROID__
log_time ts(CLOCK_MONOTONIC);
-
+ usleep(100);
log_time ts1(CLOCK_MONOTONIC);
// We fork to create a unique pid for the submitted log messages
@@ -1065,145 +1064,3 @@
TEST(logd, multiple_test_10) {
__android_log_btwrite_multiple__helper(10);
}
-
-#ifdef __ANDROID__
-// returns violating pid
-static pid_t sepolicy_rate(unsigned rate, unsigned num) {
- pid_t pid = fork();
-
- if (pid) {
- siginfo_t info = {};
- if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return -1;
- if (info.si_status) return -1;
- return pid;
- }
-
- // We may have DAC, but let's not have MAC
- if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
- int save_errno = errno;
- security_context_t context;
- getcon(&context);
- if (strcmp(context, "u:r:shell:s0")) {
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
- context, strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return -1;
- }
- }
-
- // The key here is we are root, but we are in u:r:shell:s0,
- // and the directory does not provide us DAC access
- // (eg: 0700 system system) so we trigger the pair dac_override
- // and dac_read_search on every try to get past the message
- // de-duper. We will also rotate the file name in the directory
- // as another measure.
- static const char file[] = "/data/drm/cannot_access_directory_%u";
- static const unsigned avc_requests_per_access = 2;
-
- rate /= avc_requests_per_access;
- useconds_t usec;
- if (rate == 0) {
- rate = 1;
- usec = 2000000;
- } else {
- usec = (1000000 + (rate / 2)) / rate;
- }
- num = (num + (avc_requests_per_access / 2)) / avc_requests_per_access;
-
- if (usec < 2) usec = 2;
-
- while (num > 0) {
- if (access(android::base::StringPrintf(file, num).c_str(), F_OK) == 0) {
- _exit(-1);
- // NOTREACHED
- return -1;
- }
- usleep(usec);
- --num;
- }
- _exit(0);
- // NOTREACHED
- return -1;
-}
-
-static constexpr int background_period = 10;
-
-static int count_avc(pid_t pid) {
- int count = 0;
-
- // pid=-1 skip as pid is in error
- if (pid == (pid_t)-1) return count;
-
- // pid=0 means we want to report the background count of avc: activities
- struct logger_list* logger_list =
- pid ? android_logger_list_alloc(
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)
- : android_logger_list_alloc_time(
- ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
- log_time(android_log_clockid()) -
- log_time(background_period, 0),
- 0);
- if (!logger_list) return count;
- struct logger* logger = android_logger_open(logger_list, LOG_ID_EVENTS);
- if (!logger) {
- android_logger_list_close(logger_list);
- return count;
- }
- for (;;) {
- log_msg log_msg;
-
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
- (log_msg.id() != LOG_ID_EVENTS))
- continue;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- uint32_t tag = get4LE(eventData);
- if (tag != AUDITD_LOG_TAG) continue;
-
- if (eventData[4] != EVENT_TYPE_STRING) continue;
-
- // int len = get4LE(eventData + 4 + 1);
- log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
- const char* cp = strstr(eventData + 4 + 1 + 4, "): avc: denied");
- if (!cp) continue;
-
- ++count;
- }
-
- android_logger_list_close(logger_list);
-
- return count;
-}
-#endif
-
-TEST(logd, sepolicy_rate_limiter) {
-#ifdef __ANDROID__
- int background_selinux_activity_too_high = count_avc(0);
- if (background_selinux_activity_too_high > 2) {
- GTEST_LOG_(ERROR) << "Too much background selinux activity "
- << background_selinux_activity_too_high * 60 /
- background_period
- << "/minute on the device, this test\n"
- << "can not measure the functionality of the "
- << "sepolicy rate limiter. Expect test to\n"
- << "fail as this device is in a bad state, "
- << "but is not strictly a unit test failure.";
- }
-
- static const int rate = AUDIT_RATE_LIMIT;
- static const int duration = 2;
- // Two seconds of sustained denials. Depending on the overlap in the time
- // window that the kernel is considering vs what this test is considering,
- // allow some additional denials to prevent a flaky test.
- EXPECT_LE(count_avc(sepolicy_rate(rate, rate * duration)),
- rate * duration + rate);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
deleted file mode 100644
index c3cf746..0000000
--- a/mkbootimg/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2012 The Android Open Source Project
-
-cc_library_headers {
- name: "libmkbootimg_abi_headers",
- vendor_available: true,
- export_include_dirs: ["include"],
-}
-
-cc_library_headers {
- name: "bootimg_headers",
- vendor_available: true,
- recovery_available: true,
- export_include_dirs: ["include/bootimg"],
- host_supported: true,
- target: {
- windows: {
- enabled: true,
- },
- },
-}
-
-cc_library {
- name: "libmkbootimg_abi_check",
- vendor_available: true,
- vndk: {
- enabled: true,
- },
- srcs: [
- "mkbootimg_dummy.cpp",
- ],
- header_libs: ["libmkbootimg_abi_headers"],
- export_header_lib_headers: ["libmkbootimg_abi_headers"],
-}
-
-python_defaults {
- name: "mkbootimg_defaults",
-
- version: {
- py2: {
- enabled: true,
- embedded_launcher: true,
- },
- py3: {
- enabled: false,
- embedded_launcher: false,
- },
- },
-}
-
-python_binary_host {
- name: "mkbootimg",
- defaults: ["mkbootimg_defaults"],
- srcs: [
- "mkbootimg.py",
- ],
-}
-
-python_binary_host {
- name: "unpack_bootimg",
- defaults: ["mkbootimg_defaults"],
- srcs: [
- "unpack_bootimg.py",
- ],
-}
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
deleted file mode 100644
index 3a00860..0000000
--- a/mkbootimg/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-hridya@google.com
-tbao@google.com
diff --git a/mkbootimg/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
deleted file mode 100644
index d478aba..0000000
--- a/mkbootimg/include/abi_check/mkbootimg_abi_check.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bootimg/bootimg.h>
-
-// This header has been created for the following reaons:
-// 1) In order for a change in a user defined type to be classified as API /
-// ABI breaking, it needs to be referenced by an 'exported interface'
-// (in this case the function mkbootimg_dummy).
-// 2) Since 'mkbootimg_dummy' needs to be exported, we need to have it
-// exposed through a public header.
-// 3) It is desirable not to pollute bootimg.h with interfaces which are not
-// 'used' in reality by on device binaries. Furthermore, bootimg.h might
-// be exported by a library in the future, so we must avoid polluting it.
-void mkbootimg_dummy(boot_img_hdr*);
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
deleted file mode 100644
index 4432f9e..0000000
--- a/mkbootimg/include/bootimg/bootimg.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#define BOOT_MAGIC "ANDROID!"
-#define BOOT_MAGIC_SIZE 8
-#define BOOT_NAME_SIZE 16
-#define BOOT_ARGS_SIZE 512
-#define BOOT_EXTRA_ARGS_SIZE 1024
-
-// The bootloader expects the structure of boot_img_hdr with header
-// version 0 to be as follows:
-struct boot_img_hdr_v0 {
- // Must be BOOT_MAGIC.
- uint8_t magic[BOOT_MAGIC_SIZE];
-
- uint32_t kernel_size; /* size in bytes */
- uint32_t kernel_addr; /* physical load addr */
-
- uint32_t ramdisk_size; /* size in bytes */
- uint32_t ramdisk_addr; /* physical load addr */
-
- uint32_t second_size; /* size in bytes */
- uint32_t second_addr; /* physical load addr */
-
- uint32_t tags_addr; /* physical addr for kernel tags */
- uint32_t page_size; /* flash page size we assume */
-
- // Version of the boot image header.
- uint32_t header_version;
-
- // Operating system version and security patch level.
- // For version "A.B.C" and patch level "Y-M-D":
- // (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
- // os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
- uint32_t os_version;
-
-#if __cplusplus
- void SetOsVersion(unsigned major, unsigned minor, unsigned patch) {
- os_version &= ((1 << 11) - 1);
- os_version |= (((major & 0x7f) << 25) | ((minor & 0x7f) << 18) | ((patch & 0x7f) << 11));
- }
-
- void SetOsPatchLevel(unsigned year, unsigned month) {
- os_version &= ~((1 << 11) - 1);
- os_version |= (((year - 2000) & 0x7f) << 4) | ((month & 0xf) << 0);
- }
-#endif
-
- uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
-
- uint8_t cmdline[BOOT_ARGS_SIZE];
-
- uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
-
- // Supplemental command line data; kept here to maintain
- // binary compatibility with older versions of mkbootimg.
- uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
-} __attribute__((packed));
-
-/*
- * It is expected that callers would explicitly specify which version of the
- * boot image header they need to use.
- */
-typedef struct boot_img_hdr_v0 boot_img_hdr;
-
-/* When a boot header is of version 0, the structure of boot image is as
- * follows:
- *
- * +-----------------+
- * | boot header | 1 page
- * +-----------------+
- * | kernel | n pages
- * +-----------------+
- * | ramdisk | m pages
- * +-----------------+
- * | second stage | o pages
- * +-----------------+
- *
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. second is optional (second_size == 0 -> no second)
- * 3. load each element (kernel, ramdisk, second) at
- * the specified physical address (kernel_addr, etc)
- * 4. prepare tags at tag_addr. kernel_args[] is
- * appended to the kernel commandline in the tags.
- * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 6. if second_size != 0: jump to second_addr
- * else: jump to kernel_addr
- */
-
-struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
- uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
- uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
- uint32_t header_size;
-} __attribute__((packed));
-
-/* When the boot image header has a version of 1, the structure of the boot
- * image is as follows:
- *
- * +---------------------+
- * | boot header | 1 page
- * +---------------------+
- * | kernel | n pages
- * +---------------------+
- * | ramdisk | m pages
- * +---------------------+
- * | second stage | o pages
- * +---------------------+
- * | recovery dtbo/acpio | p pages
- * +---------------------+
- * n = (kernel_size + page_size - 1) / page_size
- * m = (ramdisk_size + page_size - 1) / page_size
- * o = (second_size + page_size - 1) / page_size
- * p = (recovery_dtbo_size + page_size - 1) / page_size
- *
- * 0. all entities are page_size aligned in flash
- * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
- * devices(recovery_dtbo_size != 0)
- * 3. second is optional (second_size == 0 -> no second)
- * 4. load each element (kernel, ramdisk, second) at
- * the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery
- * dtbo/acpio and apply the correct set of overlays on the base device tree
- * depending on the hardware/product revision.
- * 6. prepare tags at tag_addr. kernel_args[] is
- * appended to the kernel commandline in the tags.
- * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 8. if second_size != 0: jump to second_addr
- * else: jump to kernel_addr
- */
diff --git a/mkbootimg/mkbootimg.py b/mkbootimg/mkbootimg.py
deleted file mode 100644
index 2eb2bab..0000000
--- a/mkbootimg/mkbootimg.py
+++ /dev/null
@@ -1,211 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from __future__ import print_function
-from sys import argv, exit, stderr
-from argparse import ArgumentParser, FileType, Action
-from os import fstat
-from struct import pack
-from hashlib import sha1
-import sys
-import re
-
-def filesize(f):
- if f is None:
- return 0
- try:
- return fstat(f.fileno()).st_size
- except OSError:
- return 0
-
-
-def update_sha(sha, f):
- if f:
- sha.update(f.read())
- f.seek(0)
- sha.update(pack('I', filesize(f)))
- else:
- sha.update(pack('I', 0))
-
-
-def pad_file(f, padding):
- pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
- f.write(pack(str(pad) + 'x'))
-
-
-def get_number_of_pages(image_size, page_size):
- """calculates the number of pages required for the image"""
- return (image_size + page_size - 1) / page_size
-
-
-def get_recovery_dtbo_offset(args):
- """calculates the offset of recovery_dtbo image in the boot image"""
- num_header_pages = 1 # header occupies a page
- num_kernel_pages = get_number_of_pages(filesize(args.kernel), args.pagesize)
- num_ramdisk_pages = get_number_of_pages(filesize(args.ramdisk), args.pagesize)
- num_second_pages = get_number_of_pages(filesize(args.second), args.pagesize)
- dtbo_offset = args.pagesize * (num_header_pages + num_kernel_pages +
- num_ramdisk_pages + num_second_pages)
- return dtbo_offset
-
-
-def write_header(args):
- BOOT_MAGIC = 'ANDROID!'.encode()
- args.output.write(pack('8s', BOOT_MAGIC))
- args.output.write(pack('10I',
- filesize(args.kernel), # size in bytes
- args.base + args.kernel_offset, # physical load addr
- filesize(args.ramdisk), # size in bytes
- args.base + args.ramdisk_offset, # physical load addr
- filesize(args.second), # size in bytes
- args.base + args.second_offset, # physical load addr
- args.base + args.tags_offset, # physical addr for kernel tags
- args.pagesize, # flash page size we assume
- args.header_version, # version of bootimage header
- (args.os_version << 11) | args.os_patch_level)) # os version and patch level
- args.output.write(pack('16s', args.board.encode())) # asciiz product name
- args.output.write(pack('512s', args.cmdline[:512].encode()))
-
- sha = sha1()
- update_sha(sha, args.kernel)
- update_sha(sha, args.ramdisk)
- update_sha(sha, args.second)
-
- if args.header_version > 0:
- update_sha(sha, args.recovery_dtbo)
-
- img_id = pack('32s', sha.digest())
-
- args.output.write(img_id)
- args.output.write(pack('1024s', args.cmdline[512:].encode()))
-
- if args.header_version > 0:
- args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
- if args.recovery_dtbo:
- args.output.write(pack('Q', get_recovery_dtbo_offset(args))) # recovery dtbo offset
- else:
- args.output.write(pack('Q', 0)) # Will be set to 0 for devices without a recovery dtbo
- args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
-
- pad_file(args.output, args.pagesize)
- return img_id
-
-
-class ValidateStrLenAction(Action):
- def __init__(self, option_strings, dest, nargs=None, **kwargs):
- if 'maxlen' not in kwargs:
- raise ValueError('maxlen must be set')
- self.maxlen = int(kwargs['maxlen'])
- del kwargs['maxlen']
- super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
-
- def __call__(self, parser, namespace, values, option_string=None):
- if len(values) > self.maxlen:
- raise ValueError('String argument too long: max {0:d}, got {1:d}'.
- format(self.maxlen, len(values)))
- setattr(namespace, self.dest, values)
-
-
-def write_padded_file(f_out, f_in, padding):
- if f_in is None:
- return
- f_out.write(f_in.read())
- pad_file(f_out, padding)
-
-
-def parse_int(x):
- return int(x, 0)
-
-def parse_os_version(x):
- match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
- if match:
- a = int(match.group(1))
- b = c = 0
- if match.lastindex >= 2:
- b = int(match.group(2))
- if match.lastindex == 3:
- c = int(match.group(3))
- # 7 bits allocated for each field
- assert a < 128
- assert b < 128
- assert c < 128
- return (a << 14) | (b << 7) | c
- return 0
-
-def parse_os_patch_level(x):
- match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
- if match:
- y = int(match.group(1)) - 2000
- m = int(match.group(2))
- # 7 bits allocated for the year, 4 bits for the month
- assert y >= 0 and y < 128
- assert m > 0 and m <= 12
- return (y << 4) | m
- return 0
-
-def parse_cmdline():
- parser = ArgumentParser()
- parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
- required=True)
- parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
- parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
- recovery_dtbo_group = parser.add_mutually_exclusive_group()
- recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
- recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
- type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
- parser.add_argument('--cmdline', help='extra arguments to be passed on the '
- 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
- parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
- parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
- parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
- parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
- default=0x00f00000)
- parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
- default=0)
- parser.add_argument('--os_patch_level', help='operating system patch level',
- type=parse_os_patch_level, default=0)
- parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
- parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
- maxlen=16)
- parser.add_argument('--pagesize', help='page size', type=parse_int,
- choices=[2**i for i in range(11,15)], default=2048)
- parser.add_argument('--id', help='print the image ID on standard output',
- action='store_true')
- parser.add_argument('--header_version', help='boot image header version', type=parse_int, default=0)
- parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
- required=True)
- return parser.parse_args()
-
-
-def write_data(args):
- write_padded_file(args.output, args.kernel, args.pagesize)
- write_padded_file(args.output, args.ramdisk, args.pagesize)
- write_padded_file(args.output, args.second, args.pagesize)
-
- if args.header_version > 0:
- write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
-
-def main():
- args = parse_cmdline()
- img_id = write_header(args)
- write_data(args)
- if args.id:
- if isinstance(img_id, str):
- # Python 2's struct.pack returns a string, but py3 returns bytes.
- img_id = [ord(x) for x in img_id]
- print('0x' + ''.join('{:02x}'.format(c) for c in img_id))
-
-if __name__ == '__main__':
- main()
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
deleted file mode 100644
index c37acd5..0000000
--- a/mkbootimg/unpack_bootimg.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2018, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""unpacks the bootimage.
-
-Extracts the kernel, ramdisk, second bootloader and recovery dtbo images.
-"""
-
-from __future__ import print_function
-from argparse import ArgumentParser, FileType
-from struct import unpack
-import os
-
-
-def create_out_dir(dir_path):
- """creates a directory 'dir_path' if it does not exist"""
- if not os.path.exists(dir_path):
- os.makedirs(dir_path)
-
-
-def extract_image(offset, size, bootimage, extracted_image_name):
- """extracts an image from the bootimage"""
- bootimage.seek(offset)
- with open(extracted_image_name, 'wb') as file_out:
- file_out.write(bootimage.read(size))
-
-
-def get_number_of_pages(image_size, page_size):
- """calculates the number of pages required for the image"""
- return (image_size + page_size - 1) / page_size
-
-
-def unpack_bootimage(args):
- """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
- boot_magic = unpack('8s', args.boot_img.read(8))
- print('boot_magic: %s' % boot_magic)
- kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
- print('kernel_size: %s' % kernel_ramdisk_second_info[0])
- print('kernel load address: %s' % kernel_ramdisk_second_info[1])
- print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
- print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
- print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
- print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
- print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
- print('page size: %s' % kernel_ramdisk_second_info[7])
- print('boot image header version: %s' % kernel_ramdisk_second_info[8])
- print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
-
- product_name = unpack('16s', args.boot_img.read(16))
- print('product name: %s' % product_name)
- cmdline = unpack('512s', args.boot_img.read(512))
- print('command line args: %s' % cmdline)
-
- args.boot_img.read(32) # ignore SHA
-
- extra_cmdline = unpack('1024s', args.boot_img.read(1024))
- print('additional command line args: %s' % extra_cmdline)
-
- kernel_size = kernel_ramdisk_second_info[0]
- ramdisk_size = kernel_ramdisk_second_info[2]
- second_size = kernel_ramdisk_second_info[4]
- page_size = kernel_ramdisk_second_info[7]
- version = kernel_ramdisk_second_info[8]
- if version > 0:
- recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
- print('recovery dtbo size: %s' % recovery_dtbo_size)
- recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0]
- print('recovery dtbo offset: %s' % recovery_dtbo_offset)
- boot_header_size = unpack('I', args.boot_img.read(4))[0]
- print('boot header size: %s' % boot_header_size)
- else:
- recovery_dtbo_size = 0
-
- # The first page contains the boot header
- num_header_pages = 1
-
- num_kernel_pages = get_number_of_pages(kernel_size, page_size)
- kernel_offset = page_size * num_header_pages # header occupies a page
- image_info_list = [(kernel_offset, kernel_size, 'kernel')]
-
- num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
- ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
- ) # header + kernel
- image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
-
- second_offset = page_size * (
- num_header_pages + num_kernel_pages + num_ramdisk_pages
- ) # header + kernel + ramdisk
- image_info_list.append((second_offset, second_size, 'second'))
-
- if recovery_dtbo_size > 0:
- image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size,
- 'recovery_dtbo'))
-
- for image_info in image_info_list:
- extract_image(image_info[0], image_info[1], args.boot_img,
- os.path.join(args.out, image_info[2]))
-
-
-def parse_cmdline():
- """parse command line arguments"""
- parser = ArgumentParser(
- description='Unpacks boot.img/recovery.img, extracts the kernel,'
- 'ramdisk, second bootloader and recovery dtbo')
- parser.add_argument(
- '--boot_img',
- help='path to boot image',
- type=FileType('rb'),
- required=True)
- parser.add_argument('--out', help='path to out binaries', default='out')
- return parser.parse_args()
-
-
-def main():
- """parse arguments and unpack boot image"""
- args = parse_cmdline()
- create_out_dir(args.out)
- unpack_bootimage(args)
-
-
-if __name__ == '__main__':
- main()
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 70f6faa..ac802b5 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -3,6 +3,7 @@
host_supported: true,
vendor_available: true,
recovery_available: true,
+ native_bridge_supported: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
@@ -12,5 +13,7 @@
"-Werror",
],
stl: "none",
+ system_shared_libs: [],
+ header_libs: ["libc_headers"],
export_include_dirs: ["include"],
}
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index f484550..33da1f1 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -585,6 +585,7 @@
{"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init", "u:object_r:boottime_prop:s0"},
+ {"ro.boottime.init.first_stage", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index aad00ad..76d6f7e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -8,6 +8,11 @@
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_REQUIRED_MODULES := fsverity_init
+
+# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
+# Since init.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
include $(BUILD_PREBUILT)
@@ -18,7 +23,6 @@
LOCAL_MODULE := init-debug.rc
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
include $(BUILD_PREBUILT)
@@ -54,6 +58,15 @@
endif
#######################################
+# fsverity_init
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= fsverity_init
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := fsverity_init.sh
+include $(BUILD_PREBUILT)
+
+#######################################
# init.environ.rc
include $(CLEAR_VARS)
@@ -76,7 +89,7 @@
EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
- EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+ EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
endif
# Put it here instead of in init.rc module definition,
@@ -84,7 +97,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data odm oem acct config storage mnt apex $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
@@ -100,10 +113,10 @@
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
endif
-ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
- LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product_services
+ifdef BOARD_USES_SYSTEM_EXTIMAGE
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
else
- LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product_services $(TARGET_ROOT_OUT)/product_services
+ LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
endif
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -150,6 +163,7 @@
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
+ $(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
@@ -200,6 +214,45 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+# Start of runtime APEX compatibility.
+#
+# Meta-comment:
+# The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
+# entries need to be associated with something that goes into /system.
+# ld.config.txt qualifies but it could be anything else in /system until soong
+# supports creation of symlinks. http://b/123333111
+#
+# Keeping the appearance of files/dirs having old locations for apps that have
+# come to rely on them.
+
+# http://b/121248172 - create a link from /system/usr/icu to
+# /apex/com.android.runtime/etc/icu so that apps can find the ICU .dat file.
+# A symlink can't overwrite a directory and the /system/usr/icu directory once
+# existed so the required structure must be created whatever we find.
+LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+
+# TODO(b/124106384): Clean up compat symlinks for ART binaries.
+ART_BINARIES := \
+ dalvikvm \
+ dalvikvm32 \
+ dalvikvm64 \
+ dex2oat \
+ dexdiag \
+ dexdump \
+ dexlist \
+ dexoptanalyzer \
+ oatdump \
+ profman \
+
+LOCAL_POST_INSTALL_CMD += && mkdir -p $(TARGET_OUT)/bin
+$(foreach b,$(ART_BINARIES), \
+ $(eval LOCAL_POST_INSTALL_CMD += \
+ && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+)
+
+# End of runtime APEX compatibilty.
+
ifeq ($(_enforce_vndk_at_runtime),true)
# for VNDK enforced devices
@@ -250,14 +303,11 @@
include $(LOCAL_PATH)/update_and_install_ld_config.mk
endef
-# For VNDK snapshot versions prior to 28, ld.config.txt is installed from the
-# prebuilt under /prebuilts/vndk
vndk_snapshots := $(wildcard prebuilts/vndk/*)
supported_vndk_snapshot_versions := \
- $(strip $(foreach ver,$(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)),\
- $(if $(call math_gt_or_eq,$(ver),28),$(ver),)))
-$(eval $(foreach ver,$(supported_vndk_snapshot_versions),\
- $(call build_versioned_ld_config,$(ver))))
+ $(strip $(patsubst prebuilts/vndk/v%,%,$(vndk_snapshots)))
+$(foreach ver,$(supported_vndk_snapshot_versions),\
+ $(eval $(call build_versioned_ld_config,$(ver))))
vndk_snapshots :=
supported_vndk_snapshot_versions :=
@@ -326,3 +376,70 @@
$(hide) echo -n > $@
$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
echo $(lib).so >> $@;)
+
+#######################################
+# vndkcore.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkcore.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(VNDK_CORE_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+ @echo "Generate: $@"
+ @mkdir -p $(dir $@)
+ $(hide) echo -n > $@
+ $(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_LIBRARIES), \
+ echo $(lib).so >> $@;)
+
+#######################################
+# vndkprivate.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkprivate.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(VNDK_PRIVATE_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+ @echo "Generate: $@"
+ @mkdir -p $(dir $@)
+ $(hide) echo -n > $@
+ $(hide) $(foreach lib,$(PRIVATE_VNDK_PRIVATE_LIBRARIES), \
+ echo $(lib).so >> $@;)
+
+#######################################
+# sanitizer.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := sanitizer.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
+ $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(UBSAN_RUNTIME_LIBRARY) \
+ $(TSAN_RUNTIME_LIBRARY) \
+ $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(2ND_UBSAN_RUNTIME_LIBRARY) \
+ $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE):
+ @echo "Generate: $@"
+ @mkdir -p $(dir $@)
+ $(hide) echo -n > $@
+ $(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
+ echo $(lib) >> $@;)
+
+#######################################
+# adb_debug.prop in debug ramdisk
+include $(CLEAR_VARS)
+LOCAL_MODULE := adb_debug.prop
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
+include $(BUILD_PREBUILT)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/adb_debug.prop b/rootdir/adb_debug.prop
new file mode 100644
index 0000000..37e2f2d
--- /dev/null
+++ b/rootdir/adb_debug.prop
@@ -0,0 +1,12 @@
+# Note: This file will be loaded with highest priority to override
+# other system properties, if a special ramdisk with "/force_debuggable"
+# is used and the device is unlocked.
+
+# Disable adb authentication to allow test automation on user build GSI
+ro.adb.secure=0
+
+# Allow 'adb root' on user build GSI
+ro.debuggable=1
+
+# Introduce this property to indicate that init has loaded adb_debug.prop
+ro.force.debuggable=1
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
new file mode 100644
index 0000000..5dc019c
--- /dev/null
+++ b/rootdir/avb/Android.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH:= $(call my-dir)
+
+#######################################
+# q-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# r-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
+
+#######################################
+# s-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+else
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
+endif
+
+include $(BUILD_PREBUILT)
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/rootdir/avb/q-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/rootdir/avb/r-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/rootdir/avb/s-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
new file mode 100644
index 0000000..e4d3d5e
--- /dev/null
+++ b/rootdir/etc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBionicTestCases"
+ }
+ ]
+}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index ca6aafe..f0b1fd2 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -6,35 +6,191 @@
# All binaries gets the same configuration 'legacy'
dir.legacy = /system
+dir.legacy = /product
dir.legacy = /vendor
dir.legacy = /odm
dir.legacy = /sbin
-# Except for /postinstall, where only /system is searched
+# Except for /postinstall, where only /system and /product are searched
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.legacy = /data
+
[legacy]
namespace.default.isolated = false
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.search.paths += /vendor/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /product/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+
+###############################################################################
+# APEX related namespaces.
+###############################################################################
+
+additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv,neuralnetworks
+namespace.default.asan.links = runtime,resolv,neuralnetworks
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default
+namespace.media.link.default.shared_libs = libbinder_ndk.so
+namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
+namespace.media.link.default.shared_libs += libdl.so
+namespace.media.link.default.shared_libs += liblog.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += libmediandk.so
+namespace.media.link.default.shared_libs += libm.so
+namespace.media.link.default.shared_libs += libvndksupport.so
+
+namespace.media.link.default.shared_libs += libclang_rt.asan-aarch64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-arm-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-i686-android.so
+namespace.media.link.default.shared_libs += libclang_rt.asan-x86_64-android.so
+namespace.media.link.default.shared_libs += libclang_rt.hwasan-aarch64-android.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
###############################################################################
# Namespace config for binaries under /postinstall.
# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# /system/lib and /product/lib in the search paths. This is because linker
+# calls realpath on the search paths and this causes selinux denial if the
+# paths (/vendor, /odm) are not allowed to the poinstall binaries.
+# There is no reason to allow the binaries to access the paths.
###############################################################################
[postinstall]
namespace.default.isolated = false
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index d3e80c9..3321425 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -20,15 +21,21 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
[system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
###############################################################################
# "default" namespace
@@ -37,10 +44,13 @@
# can't be loaded in this namespace.
###############################################################################
namespace.default.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -52,15 +62,21 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.permitted.paths += /%PRODUCT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /system/vendor/framework
+namespace.default.permitted.paths += /system/vendor/app
+namespace.default.permitted.paths += /system/vendor/priv-app
namespace.default.permitted.paths += /odm/framework
namespace.default.permitted.paths += /odm/app
namespace.default.permitted.paths += /odm/priv-app
@@ -68,18 +84,17 @@
namespace.default.permitted.paths += /%PRODUCT%/framework
namespace.default.permitted.paths += /%PRODUCT%/app
namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
+namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
+namespace.default.permitted.paths += /system/${LIB}/bootstrap
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -88,9 +103,16 @@
namespace.default.asan.permitted.paths += /system/framework
namespace.default.asan.permitted.paths += /system/app
namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /system/vendor/framework
+namespace.default.asan.permitted.paths += /system/vendor/app
+namespace.default.asan.permitted.paths += /system/vendor/priv-app
namespace.default.asan.permitted.paths += /odm/framework
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
@@ -99,11 +121,119 @@
namespace.default.asan.permitted.paths += /%PRODUCT%/framework
namespace.default.asan.permitted.paths += /%PRODUCT%/app
namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
+namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv,neuralnetworks
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default,neuralnetworks
+namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# LLNDK library moved into apex
+namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
###############################################################################
# "sphal" namespace
@@ -119,13 +249,17 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
namespace.sphal.search.paths += /vendor/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}/hw
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -138,16 +272,22 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk,neuralnetworks
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
+# LLNDK library moved into apex
+namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
###############################################################################
# "rs" namespace
@@ -168,6 +308,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -187,9 +328,9 @@
namespace.rs.asan.permitted.paths += /vendor/${LIB}
namespace.rs.asan.permitted.paths += /data
-namespace.rs.links = default,vndk
+namespace.rs.links = default,vndk,neuralnetworks
-namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
# namespace because RS framework libs are using them.
@@ -197,12 +338,18 @@
namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+# LLNDK library moved into apex
+namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+
###############################################################################
# "vndk" namespace
#
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -213,6 +360,8 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
@@ -238,7 +387,7 @@
# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
# "sphal" namespace for vendor libs. The ordering matters. The "default"
# namespace has higher priority than the "sphal" namespace.
-namespace.vndk.links = default,sphal
+namespace.vndk.links = default,sphal,runtime,neuralnetworks
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
@@ -246,9 +395,34 @@
namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
# Allow VNDK-SP extensions to use vendor libraries
namespace.vndk.link.sphal.allow_all_shared_libs = true
+# LLNDK library moved into apex
+namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
###############################################################################
# Namespace config for vendor processes. In O, no restriction is enforced for
# them. However, in O-MR1, access to /system/${LIB} will not be allowed to
@@ -256,7 +430,7 @@
# (LL-NDK only) access.
###############################################################################
[vendor]
-additional.namespaces = system,vndk
+additional.namespaces = runtime,system,neuralnetworks,vndk%VNDK_IN_SYSTEM_NS%
###############################################################################
# "default" namespace
@@ -267,6 +441,9 @@
# partition (VNDK and LLNDK libraries) are not loaded here but from the
# separate namespace 'system'. The delegation to the system namespace is done
# via the 'namespace.default.link.system.shared_libs' property below.
+#
+# '#VNDK27#' TAG is only for building ld.config.27.txt for backward
+# compatibility. (TODO:b/123390078) Move them to a separate file.
###############################################################################
namespace.default.isolated = true
namespace.default.visible = true
@@ -276,22 +453,51 @@
namespace.default.permitted.paths = /odm
namespace.default.permitted.paths += /vendor
+namespace.default.permitted.paths += /system/vendor
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/hw
+#VNDK27#namespace.default.search.paths += /vendor/${LIB}/egl
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths += /vendor/${LIB}/hw
+#VNDK27#namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/egl
+#VNDK27#namespace.default.asan.search.paths += /vendor/${LIB}/egl
namespace.default.asan.permitted.paths = /data/asan/odm
namespace.default.asan.permitted.paths += /odm
namespace.default.asan.permitted.paths += /data/asan/vendor
namespace.default.asan.permitted.paths += /vendor
-namespace.default.links = system,vndk
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%,runtime,neuralnetworks
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.default.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.default.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
namespace.default.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
namespace.default.link.vndk.shared_libs += %VNDK_CORE_LIBRARIES%
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = system
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.system.allow_all_shared_libs = true
+
+
###############################################################################
# "vndk" namespace
#
@@ -322,13 +528,23 @@
# When these NDK libs are required inside this namespace, then it is redirected
# to the system namespace. This is possible since their ABI is stable across
-# Android releases.
-namespace.vndk.links = system,default
+# Android releases. The links here should be identical to that of the
+# 'vndk_in_system' namespace, except for the link between 'vndk' and
+# 'vndk_in_system'.
+namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%,runtime,neuralnetworks
+
namespace.vndk.link.system.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.vndk.link.default.allow_all_shared_libs = true
+namespace.vndk.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk.link.vndk_in_system.shared_libs = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
+# LLNDK library moved into apex
+namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
###############################################################################
# "system" namespace
#
@@ -338,26 +554,246 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.system.search.paths += /%PRODUCT%/${LIB}
-namespace.system.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
-namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.system.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.system.links = runtime
+namespace.system.link.runtime.shared_libs = libdexfile_external.so
+namespace.system.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.system.link.runtime.shared_libs += libicui18n.so
+namespace.system.link.runtime.shared_libs += libicuuc.so
+namespace.system.link.runtime.shared_libs += libnativebridge.so
+namespace.system.link.runtime.shared_libs += libnativehelper.so
+namespace.system.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.system.link.runtime.shared_libs += libandroidicu.so
+namespace.system.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+###############################################################################
+# "vndk_in_system" namespace
+#
+# This namespace is where no-vendor-variant VNDK libraries are loaded for a
+# vendor process. Note that we do not simply export these libraries from
+# "system" namespace, because in some case both the core variant and the
+# vendor variant of a VNDK library may be loaded. In such case, we do not
+# want to eliminate double-loading because doing so means the global states
+# of the library would be shared.
+#
+# Only the no-vendor-variant VNDK libraries are whitelisted in this namespace.
+# This is to ensure that we do not load libraries needed by no-vendor-variant
+# VNDK libraries into vndk_in_system namespace.
+###############################################################################
+namespace.vndk_in_system.isolated = true
+namespace.vndk_in_system.visible = true
+
+# The search paths here should be kept the same as that of the 'system'
+# namespace.
+namespace.vndk_in_system.search.paths = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
+
+namespace.vndk_in_system.asan.search.paths = /data/asan/system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /system/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%PRODUCT%/${LIB}
+
+namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
+
+# The links here should be identical to that of the 'vndk' namespace, with the
+# following exception:
+# 1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
+# 2. 'vndk_in_system' does not need to link to 'default', as any library that
+# requires anything vendor would not be a vndk_in_system library.
+namespace.vndk_in_system.links = vndk,system,runtime,neuralnetworks
+namespace.vndk_in_system.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.system.shared_libs = %LLNDK_LIBRARIES%
+namespace.vndk_in_system.link.system.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.vndk_in_system.link.vndk.allow_all_shared_libs = true
+namespace.vndk_in_system.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
+
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+namespace.default.links = runtime,resolv,neuralnetworks
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default,neuralnetworks
+namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# LLNDK library moved into apex
+namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
###############################################################################
# Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default namespace is defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
###############################################################################
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 7e354ac..0bb60ab 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -20,15 +21,21 @@
dir.vendor = /data/benchmarktest/vendor
dir.vendor = /data/benchmarktest64/vendor
-dir.system = /data/nativetest
-dir.system = /data/nativetest64
-dir.system = /data/benchmarktest
-dir.system = /data/benchmarktest64
+dir.unrestricted = /data/nativetest/unrestricted
+dir.unrestricted = /data/nativetest64/unrestricted
+
+# TODO(b/123864775): Ensure tests are run from /data/nativetest{,64} or (if
+# necessary) the unrestricted subdirs above. Then clean this up.
+dir.unrestricted = /data/local/tmp
dir.postinstall = /postinstall
+# Fallback entry to provide APEX namespace lookups for binaries anywhere else.
+# This must be last.
+dir.system = /data
+
[system]
-additional.namespaces = sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
###############################################################################
# "default" namespace
@@ -37,23 +44,134 @@
# partitions are also allowed temporarily.
###############################################################################
namespace.default.isolated = false
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# If a shared library or an executable requests a shared library that
+# cannot be loaded into the default namespace, the dynamic linker tries
+# to load the shared library from the runtime namespace. And then, if the
+# shared library cannot be loaded from the runtime namespace either, the
+# dynamic linker tries to load the shared library from the resolv namespace.
+# Finally, if all attempts fail, the dynamic linker returns an error.
+namespace.default.links = runtime,resolv,neuralnetworks
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+
+# When libnetd_resolv.so can't be found in the default namespace, search for it
+# in the resolv namespace. Don't allow any other libraries from the resolv namespace
+# to be loaded in the default namespace.
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace pulls in externally accessible libs from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# Need allow_all_shared_libs because libart.so can dlopen oat files in
+# /system/framework and /data.
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default,neuralnetworks
+namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# LLNDK library moved into apex
+namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+namespace.conscrypt.link.default.shared_libs += liblog.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+namespace.resolv.link.default.shared_libs += liblog.so
+namespace.resolv.link.default.shared_libs += libvndksupport.so
###############################################################################
# "sphal" namespace
@@ -69,13 +187,17 @@
# Note that there is no link from the default namespace to this namespace.
###############################################################################
namespace.sphal.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.sphal.visible = true
namespace.sphal.search.paths = /odm/${LIB}
namespace.sphal.search.paths += /vendor/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}/hw
namespace.sphal.permitted.paths = /odm/${LIB}
namespace.sphal.permitted.paths += /vendor/${LIB}
+namespace.sphal.permitted.paths += /system/vendor/${LIB}
namespace.sphal.asan.search.paths = /data/asan/odm/${LIB}
namespace.sphal.asan.search.paths += /odm/${LIB}
@@ -88,16 +210,21 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk,neuralnetworks
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
+# LLNDK library moved into apex
+namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
###############################################################################
# "rs" namespace
@@ -118,6 +245,7 @@
namespace.rs.permitted.paths = /odm/${LIB}
namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
namespace.rs.permitted.paths += /data
namespace.rs.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
@@ -137,9 +265,9 @@
namespace.rs.asan.permitted.paths += /vendor/${LIB}
namespace.rs.asan.permitted.paths += /data
-namespace.rs.links = default,vndk
+namespace.rs.links = default,vndk,neuralnetworks
-namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
# namespace because RS framework libs are using them.
@@ -147,12 +275,17 @@
namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+# LLNDK library moved into apex
+namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
###############################################################################
# "vndk" namespace
#
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
namespace.vndk.visible = true
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
@@ -163,6 +296,7 @@
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+namespace.vndk.permitted.paths += /system/vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
@@ -188,9 +322,32 @@
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
# Android releases.
-namespace.vndk.links = default
+namespace.vndk.links = default,neuralnetworks
+
namespace.vndk.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
###############################################################################
# Namespace config for vendor processes. In O, no restriction is enforced for
@@ -199,6 +356,8 @@
# (LL-NDK only) access.
###############################################################################
[vendor]
+additional.namespaces = runtime,neuralnetworks
+
namespace.default.isolated = false
namespace.default.search.paths = /odm/${LIB}
@@ -208,12 +367,13 @@
namespace.default.search.paths += /vendor/${LIB}/vndk
namespace.default.search.paths += /vendor/${LIB}/vndk-sp
-# Access to system libraries are allowed
-namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
+# Access to system libraries is allowed
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+# Put /system/lib/vndk at the last search order in vndk_lite for GSI
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -227,27 +387,217 @@
namespace.default.asan.search.paths += /vendor/${LIB}/vndk
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.default.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
-namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
+
+namespace.default.links = runtime,neuralnetworks
+namespace.default.link.runtime.shared_libs = libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+# Workaround for b/124772622
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+namespace.runtime.link.default.allow_all_shared_libs = true
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
+
+###############################################################################
+# Namespace config for native tests that need access to both system and vendor
+# libraries. This replicates the default linker config (done by
+# init_default_namespace_no_config in bionic/linker/linker.cpp), except that it
+# includes the requisite namespace setup for APEXes.
+###############################################################################
+[unrestricted]
+additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
+
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.default.visible = true
+
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+namespace.default.asan.search.paths = /data/asan/system/${LIB}
+namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/odm/${LIB}
+namespace.default.asan.search.paths += /odm/${LIB}
+namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.default.asan.search.paths += /vendor/${LIB}
+
+# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+namespace.default.links = runtime,resolv,neuralnetworks
+namespace.default.link.runtime.shared_libs = libandroidicu.so
+namespace.default.link.runtime.shared_libs += libdexfile_external.so
+namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
+namespace.default.link.runtime.shared_libs += libnativebridge.so
+namespace.default.link.runtime.shared_libs += libnativehelper.so
+namespace.default.link.runtime.shared_libs += libnativeloader.so
+
+# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
+namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+namespace.default.link.resolv.shared_libs = libnetd_resolv.so
+
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "runtime" APEX namespace
+#
+# This namespace exposes externally accessible libraries from the Runtime APEX.
+# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.runtime.isolated = true
+# Visible to allow links to be created at runtime, e.g. through
+# android_link_namespaces in libnativeloader.
+namespace.runtime.visible = true
+
+namespace.runtime.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.runtime/${LIB}
+namespace.runtime.links = default
+# TODO(b/130340935): Use a dynamically created linker namespace similar to
+# classloader-namespace for oat files, and tighten this up.
+
+###############################################################################
+# "media" APEX namespace
+#
+# This namespace is for libraries within the media APEX.
+###############################################################################
+namespace.media.isolated = true
+namespace.media.visible = true
+
+namespace.media.search.paths = /apex/com.android.media/${LIB}
+namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
+
+namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
+
+namespace.media.links = default,neuralnetworks
+namespace.media.link.default.shared_libs = %LLNDK_LIBRARIES%
+namespace.media.link.default.shared_libs += libbinder_ndk.so
+namespace.media.link.default.shared_libs += libmediametrics.so
+namespace.media.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+
+# LLNDK library moved into apex
+namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "conscrypt" APEX namespace
+#
+# This namespace is for libraries within the conscrypt APEX.
+# Keep in sync with the "conscrypt" namespace in art/build/apex/ld.config.txt.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
+namespace.conscrypt.links = runtime,default
+namespace.conscrypt.link.runtime.shared_libs = libandroidio.so
+namespace.conscrypt.link.default.shared_libs = libc.so
+namespace.conscrypt.link.default.shared_libs += libm.so
+namespace.conscrypt.link.default.shared_libs += libdl.so
+
+###############################################################################
+# "resolv" APEX namespace
+#
+# This namespace is for libraries within the resolv APEX.
+###############################################################################
+namespace.resolv.isolated = true
+namespace.resolv.visible = true
+
+namespace.resolv.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
+namespace.resolv.links = default
+namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
+namespace.resolv.link.default.shared_libs += libm.so
+namespace.resolv.link.default.shared_libs += libdl.so
+namespace.resolv.link.default.shared_libs += libbinder_ndk.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
###############################################################################
# Namespace config for binaries under /postinstall.
-# Only one default namespace is defined and it has no directories other than
-# /system/lib in the search paths. This is because linker calls realpath on the
-# search paths and this causes selinux denial if the paths (/vendor, /odm) are
-# not allowed to the poinstall binaries. There is no reason to allow the
-# binaries to access the paths.
+# Only default namespace is defined and default has no directories
+# other than /system/lib in the search paths. This is because linker calls
+# realpath on the search paths and this causes selinux denial if the paths
+# (/vendor, /odm) are not allowed to the postinstall binaries. There is no
+# reason to allow the binaries to access the paths.
###############################################################################
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
new file mode 100644
index 0000000..4fee15f
--- /dev/null
+++ b/rootdir/fsverity_init.sh
@@ -0,0 +1,32 @@
+#!/system/bin/sh
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Enforce fsverity signature checking
+echo 1 > /proc/sys/fs/verity/require_signatures
+
+# Load all keys
+for cert in /product/etc/security/fsverity/*.der; do
+ /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
+ log -p e -t fsverity_init "Failed to load $cert"
+done
+
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+ # Prevent future key links to .fs-verity keyring
+ /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+ log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 4576776..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -1,13 +1,16 @@
# set up the global environment
-on init
+on early-init
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ANDROID_STORAGE /storage
+ export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+ export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
export EXTERNAL_STORAGE /sdcard
export ASEC_MOUNTPOINT /mnt/asec
export BOOTCLASSPATH %BOOTCLASSPATH%
+ export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
%EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 025e3c3..d22e9a7 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -11,10 +11,8 @@
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
+# Cgroups are mounted right before early-init using list from /etc/cgroups.json
on early-init
- # Set init and its forked children's oom_adj.
- write /proc/1/oom_score_adj -1000
-
# Disable sysrq from keyboard
write /proc/sys/kernel/sysrq 0
@@ -24,14 +22,8 @@
# Set the security context of /postinstall if present.
restorecon /postinstall
- # Mount cgroup mount point for cpu accounting
- mount cgroup none /acct nodev noexec nosuid cpuacct
- chmod 0555 /acct
mkdir /acct/uid
- # root memory control cgroup, used by lmkd
- mkdir /dev/memcg 0700 root system
- mount cgroup none /dev/memcg nodev noexec nosuid memory
# memory.pressure_level used by lmkd
chown root system /dev/memcg/memory.pressure_level
chmod 0040 /dev/memcg/memory.pressure_level
@@ -40,14 +32,32 @@
# cgroup for system_server and surfaceflinger
mkdir /dev/memcg/system 0550 system system
+ # set RLIMIT_NICE to allow priorities from 19 to -20
+ setrlimit nice 40 40
+
+ # Allow up to 32K FDs per process
+ setrlimit nofile 32768 32768
+
+ # Create directory to keep ld.config.txt
+ mkdir /dev/linkerconfig 0755
+
+ # Generate ld.config.txt for early executed processes
+ exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
+ chmod 444 /dev/linkerconfig/ld.config.txt
+
start ueventd
+ # Run apexd-bootstrap so that APEXes that provide critical libraries
+ # become available. Note that this is executed as exec_start to ensure that
+ # the libraries are available to the processes started after this statement.
+ exec_start apexd-bootstrap
+
on init
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
- copy /default.prop /dev/urandom
+ copy /system/etc/prop.default /dev/urandom
symlink /proc/self/fd/0 /dev/stdin
symlink /proc/self/fd/1 /dev/stdout
@@ -63,8 +73,6 @@
symlink /system/vendor /vendor
# Create energy-aware scheduler tuning nodes
- mkdir /dev/stune
- mount cgroup none /dev/stune nodev noexec nosuid schedtune
mkdir /dev/stune/foreground
mkdir /dev/stune/background
mkdir /dev/stune/top-app
@@ -85,6 +93,21 @@
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
+ # Create blkio group and apply initial settings.
+ # This feature needs kernel to support it, and the
+ # device's init.rc must actually set the correct values.
+ mkdir /dev/blkio/background
+ chown system system /dev/blkio
+ chown system system /dev/blkio/background
+ chown system system /dev/blkio/tasks
+ chown system system /dev/blkio/background/tasks
+ chmod 0664 /dev/blkio/tasks
+ chmod 0664 /dev/blkio/background/tasks
+ write /dev/blkio/blkio.weight 1000
+ write /dev/blkio/background/blkio.weight 500
+ write /dev/blkio/blkio.group_idle 0
+ write /dev/blkio/background/blkio.group_idle 0
+
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
@@ -109,6 +132,8 @@
mkdir /mnt/runtime/read/self 0755 root root
mkdir /mnt/runtime/write 0755 root root
mkdir /mnt/runtime/write/self 0755 root root
+ mkdir /mnt/runtime/full 0755 root root
+ mkdir /mnt/runtime/full/self 0755 root root
# Symlink to keep legacy apps working in multi-user world
symlink /storage/self/primary /sdcard
@@ -156,8 +181,6 @@
chmod 0400 /proc/net/fib_trie
# Create cgroup mount points for process groups
- mkdir /dev/cpuctl
- mount cgroup none /dev/cpuctl nodev noexec nosuid cpu
chown system system /dev/cpuctl
chown system system /dev/cpuctl/tasks
chmod 0666 /dev/cpuctl/tasks
@@ -165,9 +188,6 @@
write /dev/cpuctl/cpu.rt_runtime_us 950000
# sets up initial cpusets for ActivityManager
- mkdir /dev/cpuset
- mount cpuset none /dev/cpuset nodev noexec nosuid
-
# this ensures that the cpusets are present and usable, but the device's
# init.rc must actually set the correct cpus
mkdir /dev/cpuset/foreground
@@ -218,6 +238,9 @@
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
+ # make the PSI monitor accessible to others
+ chown system system /proc/pressure/memory
+ chmod 0664 /proc/pressure/memory
# qtaguid will limit access to specific data based on group memberships.
# net_bw_acct grants impersonation of socket owners.
@@ -229,8 +252,6 @@
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
- mkdir /dev/cg2_bpf
- mount cgroup2 cg2_bpf /dev/cg2_bpf nodev noexec nosuid
chown root root /dev/cg2_bpf
chmod 0600 /dev/cg2_bpf
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
@@ -259,24 +280,29 @@
export DOWNLOAD_CACHE /data/cache
- # set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit nice 40 40
-
- # Allow up to 32K FDs per process
- setrlimit nofile 32768 32768
-
# This allows the ledtrig-transient properties to be created here so
# that they can be chown'd to system:system later on boot
write /sys/class/leds/vibrator/trigger "transient"
- # Setup APEX mount point and its security context
- mount tmpfs tmpfs /apex nodev noexec nosuid
- chmod 0755 /apex
- chown root root /apex
- restorecon /apex
+ # This is used by Bionic to select optimized routines.
+ write /dev/cpu_variant:${ro.bionic.arch} ${ro.bionic.cpu_variant}
+ chmod 0444 /dev/cpu_variant:${ro.bionic.arch}
+ write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}
+ chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}
+
+ # Allow system processes to read / write power state.
+ chown system system /sys/power/state
+ chown system system /sys/power/wakeup_count
+ chmod 0660 /sys/power/state
+
+ chown radio wakelock /sys/power/wake_lock
+ chown radio wakelock /sys/power/wake_unlock
+ chmod 0660 /sys/power/wake_lock
+ chmod 0660 /sys/power/wake_unlock
# Start logd before any other services run to ensure we capture all of their logs.
start logd
+
# Start essential services.
start servicemanager
start hwservicemanager
@@ -331,21 +357,16 @@
trigger early-boot
trigger boot
-on post-fs
- # Load properties from
- # /system/build.prop,
- # /odm/build.prop,
- # /vendor/build.prop and
- # /factory/factory.prop
- load_system_props
+on early-fs
+ # Once metadata has been mounted, we'll need vold to deal with userdata checkpointing
start vold
+
+on post-fs
exec - system system -- /system/bin/vdc checkpoint markBootAttempt
# Once everything is setup, no need to modify /.
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
- # Mount shared so changes propagate into child namespaces
- mount rootfs rootfs / shared rec
# Mount default storage into root namespace
mount none /mnt/runtime/default /storage bind rec
mount none none /storage slave rec
@@ -392,7 +413,11 @@
restorecon_recursive /metadata
mkdir /metadata/vold
chmod 0700 /metadata/vold
+ mkdir /metadata/password_slots 0771 root system
+ mkdir /metadata/ota 0700 root system
+ mkdir /metadata/apex 0700 root system
+ mkdir /metadata/apex/sessions 0700 root system
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@@ -401,7 +426,13 @@
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
+ # Check and mark a successful boot, before mounting userdata with mount_all.
+ # No-op for non-A/B device.
+ exec_start update_verifier_nonencrypted
+
on post-fs-data
+ mark_post_data
+
# Start checkpoint before we touch data
start vold
exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint
@@ -420,7 +451,20 @@
mkdir /data/bootchart 0755 shell shell
bootchart start
- # Start apexd as soon as we can
+ # Load fsverity keys. This needs to happen before apexd, as post-install of
+ # APEXes may rely on keys.
+ exec -- /system/bin/fsverity_init
+
+ # Make sure that apexd is started in the default namespace
+ enter_default_mount_ns
+
+ # /data/apex is now available. Start apexd to scan and activate APEXes.
+ mkdir /data/apex 0750 root system
+ mkdir /data/apex/active 0750 root system
+ mkdir /data/apex/backup 0700 root system
+ mkdir /data/apex/hashtree 0700 root system
+ mkdir /data/apex/sessions 0700 root system
+ mkdir /data/app-staging 0750 system system
start apexd
# Avoid predictable entropy pool. Carry over entropy from previous boot.
@@ -468,7 +512,6 @@
mkdir /data/misc/ethernet 0770 system system
mkdir /data/misc/dhcp 0770 dhcp dhcp
mkdir /data/misc/user 0771 root root
- mkdir /data/misc/perfprofd 0775 root root
# give system access to wpa_supplicant.conf for backup and restore
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
mkdir /data/local 0751 root root
@@ -538,8 +581,6 @@
mkdir /data/anr 0775 system system
- mkdir /data/apex 0770 root root
-
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc
mkdir /data/nfc/param 0770 nfc nfc
@@ -572,13 +613,18 @@
mkdir /data/cache/backup_stage 0700 system system
mkdir /data/cache/backup 0700 system system
+ # Wait for apexd to finish activating APEXes before starting more processes.
+ wait_for_prop apexd.status ready
+ parse_apex_configs
+
init_user0
# Set SELinux security contexts on upgrade or policy update.
restorecon --recursive --skip-ce /data
- # Check any timezone data in /data is newer than the copy in /system, delete if not.
- exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+ # Check any timezone data in /data is newer than the copy in the time zone data
+ # module, delete if not.
+ exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
# If there is no post-fs-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
@@ -586,25 +632,28 @@
# Set indication (checked by vold) that we have finished this action
#setprop vold.post_fs_data_done 1
+ # sys.memfd_use set to false by default, which keeps it disabled
+ # until it is confirmed that apps and vendor processes don't make
+ # IOCTLs on ashmem fds any more.
+ setprop sys.use_memfd false
+
+ # Set fscklog permission
+ chown root system /dev/fscklogs/log
+ chmod 0770 /dev/fscklogs/log
+
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
@@ -632,6 +681,12 @@
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
+ # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs
+ # to avoid power consumption when system becomes mostly idle. Be careful
+ # to make it too large, since it may bring userdata loss, if they
+ # are not aware of using fsync()/sync() to prepare sudden power-cut.
+ write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
+
# Permissions for System Server and daemons.
chown radio system /sys/android_power/state
chown radio system /sys/android_power/request_state
@@ -639,13 +694,6 @@
chown radio system /sys/android_power/acquire_partial_wake_lock
chown radio system /sys/android_power/release_wake_lock
chown system system /sys/power/autosleep
- chown system system /sys/power/state
- chown system system /sys/power/wakeup_count
- chown radio wakelock /sys/power/wake_lock
- chown radio wakelock /sys/power/wake_unlock
- chmod 0660 /sys/power/state
- chmod 0660 /sys/power/wake_lock
- chmod 0660 /sys/power/wake_unlock
chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate
@@ -718,9 +766,6 @@
on charger
class_start charger
-on property:vold.decrypt=trigger_reset_main
- class_reset main
-
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
start logd
@@ -731,13 +776,13 @@
trigger zygote-start
on property:vold.decrypt=trigger_restart_min_framework
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
exec_start update_verifier
+ class_start_post_data hal
+ class_start_post_data core
class_start main
class_start late_start
setprop service.bootanim.exit 0
@@ -746,6 +791,8 @@
on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
+ class_reset_post_data core
+ class_reset_post_data hal
on property:sys.boot_completed=1
bootchart stop
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ac87979..f8e680d 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -4,6 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
+ socket blastula_pool stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index a535846..0235370 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -4,6 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
+ socket blastula_pool stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -19,5 +20,6 @@
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
+ socket blastula_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 6fc810b..3f3cc15 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -4,6 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
+ socket blastula_pool stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 7ddd52e..fae38c9 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -4,6 +4,7 @@
user root
group root readproc reserved_disk
socket zygote stream 660 root system
+ socket blastula_pool stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
@@ -19,5 +20,6 @@
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
+ socket blastula_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 2f85dec..451f5ad 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,7 +1,5 @@
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
-
-subsystem adf
- devname uevent_devname
+uevent_socket_rcvbuf_size 16M
subsystem graphics
devname uevent_devpath
@@ -11,26 +9,10 @@
devname uevent_devpath
dirname /dev/dri
-subsystem oncrpc
- devname uevent_devpath
- dirname /dev/oncrpc
-
-subsystem adsp
- devname uevent_devpath
- dirname /dev/adsp
-
-subsystem msm_camera
- devname uevent_devpath
- dirname /dev/msm_camera
-
subsystem input
devname uevent_devpath
dirname /dev/input
-subsystem mtd
- devname uevent_devpath
- dirname /dev/mtd
-
subsystem sound
devname uevent_devpath
dirname /dev/snd
@@ -58,73 +40,24 @@
/dev/pmsg0 0222 root log
-# the msm hw3d client device node is world writable/readable.
-/dev/msm_hw3dc 0666 root root
-
-# gpu driver for adreno200 is globally accessible
-/dev/kgsl 0666 root root
-
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
# these should not be world writable
-/dev/diag 0660 radio radio
-/dev/diag_arm9 0660 radio radio
-/dev/ttyMSM0 0600 bluetooth bluetooth
/dev/uhid 0660 uhid uhid
-/dev/uinput 0660 system bluetooth
-/dev/alarm 0664 system radio
+/dev/uinput 0660 uhid uhid
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
-/dev/msm_hw3dm 0660 system graphics
/dev/input/* 0660 root input
/dev/v4l-touch* 0660 root input
-/dev/eac 0660 root audio
-/dev/cam 0660 root camera
-/dev/pmem 0660 system graphics
-/dev/pmem_adsp* 0660 system audio
-/dev/pmem_camera* 0660 system camera
-/dev/oncrpc/* 0660 root system
-/dev/adsp/* 0660 system audio
/dev/snd/* 0660 system audio
-/dev/mt9t013 0660 system system
-/dev/msm_camera/* 0660 system system
-/dev/akm8976_daemon 0640 compass system
-/dev/akm8976_aot 0640 compass system
-/dev/akm8973_daemon 0640 compass system
-/dev/akm8973_aot 0640 compass system
-/dev/bma150 0640 compass system
-/dev/cm3602 0640 compass system
-/dev/akm8976_pffd 0640 compass system
-/dev/lightsensor 0640 system system
-/dev/msm_pcm_out* 0660 system audio
-/dev/msm_pcm_in* 0660 system audio
-/dev/msm_pcm_ctl* 0660 system audio
-/dev/msm_snd* 0660 system audio
-/dev/msm_mp3* 0660 system audio
-/dev/audience_a1026* 0660 system audio
-/dev/tpa2018d1* 0660 system audio
-/dev/msm_audpre 0660 system audio
-/dev/msm_audio_ctl 0660 system audio
-/dev/htc-acoustic 0660 system audio
-/dev/vdec 0660 system audio
-/dev/q6venc 0660 system audio
-/dev/snd/dsp 0660 system audio
-/dev/snd/dsp1 0660 system audio
-/dev/snd/mixer 0660 system audio
-/dev/smd0 0640 radio radio
-/dev/qmi 0640 radio radio
-/dev/qmi0 0640 radio radio
-/dev/qmi1 0640 radio radio
-/dev/qmi2 0640 radio radio
/dev/bus/usb/* 0660 root usb
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn
# CDMA radio interface MUX
-/dev/ts0710mux* 0640 radio radio
/dev/ppp 0660 radio vpn
# sysfs properties
@@ -134,6 +67,3 @@
/sys/devices/virtual/usb_composite/* enable 0664 root system
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
-
-# DVB API device nodes
-/dev/dvb* 0660 root system
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 56a30b2..dbe60e5 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -23,6 +23,11 @@
lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
libz_is_llndk := $(strip $(libz_is_llndk))
+my_vndk_use_core_variant := $(TARGET_VNDK_USE_CORE_VARIANT)
+ifeq ($(lib_list_from_prebuilts),true)
+my_vndk_use_core_variant := false
+endif
+
compatibility_check_script := \
$(LOCAL_PATH)/ld_config_backward_compatibility_check.py
intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
@@ -35,12 +40,18 @@
vndksp_libraries_file := $(library_lists_dir)/vndksp.libraries.$(vndk_version).txt
vndkcore_libraries_file := $(library_lists_dir)/vndkcore.libraries.txt
vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.txt
+llndk_moved_to_apex_libraries_file := $(library_lists_dir)/llndkinapex.libraries.txt
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
+endif
sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
$(UBSAN_RUNTIME_LIBRARY) \
$(TSAN_RUNTIME_LIBRARY) \
$(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
$(2ND_UBSAN_RUNTIME_LIBRARY) \
$(2ND_TSAN_RUNTIME_LIBRARY)))
# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
@@ -55,6 +66,10 @@
vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
endif
+# LLNDK libraries that has been moved to an apex package and no longer are present on
+# /system image.
+llndk_libraries_moved_to_apex_list:=$(LLNDK_MOVED_TO_APEX_LIBRARIES)
+
# $(1): list of libraries
# $(2): output file to write the list of libraries to
define write-libs-to-file
@@ -66,6 +81,9 @@
$(eval $(call write-libs-to-file,$(vndksp_libraries_list),$(vndksp_libraries_file)))
$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),$(vndkcore_libraries_file)))
$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),$(vndkprivate_libraries_file)))
+ifeq ($(my_vndk_use_core_variant),true)
+$(eval $(call write-libs-to-file,$(VNDK_USING_CORE_VARIANT_LIBRARIES),$(vndk_using_core_variant_libraries_file)))
+endif
endif # ifneq ($(lib_list_from_prebuilts),true)
# Given a file with a list of libs, filter-out the VNDK private libraries
@@ -75,9 +93,17 @@
# $(2): output file with the filtered list of lib names
$(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
paste -sd ":" $(1) > $(2) && \
- cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | xargs -n 1 -I privatelib bash -c "sed -i.bak 's/privatelib//' $(2)" && \
+ while read -r privatelib; do sed -i.bak "s/$$privatelib//" $(2) ; done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
rm -f $(2).bak
+
+# # Given a file with a list of libs in "a:b:c" format, filter-out the LLNDK libraries migrated into apex file
+# # and write resulting list to a new file in "a:b:c" format
+ $(LOCAL_BUILT_MODULE): private-filter-out-llndk-in-apex-libs = \
+ for lib in $(PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST); do sed -i.bak s/$$lib.so// $(1); done && \
+ sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(1) && \
+ rm -f $(1).bak
+
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SP_LIBRARIES_FILE := $(vndksp_libraries_file)
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES_FILE := $(vndkcore_libraries_file)
@@ -86,11 +112,17 @@
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
$(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
$(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_TAG := \#VNDK$(vndk_version)\#
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST := $(llndk_libraries_moved_to_apex_list)
deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
$(vndkprivate_libraries_file)
ifeq ($(check_backward_compatibility),true)
deps += $(compatibility_check_script)
endif
+ifeq ($(my_vndk_use_core_variant),true)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
+deps += $(vndk_using_core_variant_libraries_file)
+endif
$(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
@echo "Generate: $< -> $@"
@@ -100,22 +132,40 @@
endif
@mkdir -p $(dir $@)
$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
+ $(call private-filter-out-llndk-in-apex-libs,$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
$(call private-filter-out-private-libs,$(PRIVATE_VNDK_SP_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)
$(hide) sed -i.bak -e "s?%VNDK_SAMEPROCESS_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)?g" $@
$(call private-filter-out-private-libs,$(PRIVATE_VNDK_CORE_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)
$(hide) sed -i.bak -e "s?%VNDK_CORE_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndkcore_filtered)?g" $@
+ifeq ($(my_vndk_use_core_variant),true)
+ $(call private-filter-out-private-libs,$(PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)
+ $(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%?,vndk_in_system?g" $@
+ $(hide) sed -i.bak -e "s?%VNDK_USING_CORE_VARIANT_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndk_using_core_variant_filtered)?g" $@
+else
+ $(hide) sed -i.bak -e "s?%VNDK_IN_SYSTEM_NS%??g" $@
+ # Unlike LLNDK or VNDK-SP, VNDK_USING_CORE_VARIANT_LIBRARIES can be nothing
+ # if TARGET_VNDK_USE_CORE_VARIANT is not set. In this case, we need to remove
+ # the entire line in the linker config so that we are not left with a line
+ # like:
+ # namespace.vndk.link.vndk_in_system.shared_libs =
+ $(hide) sed -i.bak -e 's?^.*= %VNDK_USING_CORE_VARIANT_LIBRARIES%$$??' $@
+endif
+
$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
- cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
- xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
+ while read -r privatelib; \
+ do (grep $$privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk ; \
+ done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
- $(hide) sed -i.bak -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
- $(hide) sed -i.bak -e 's?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g' $@
- $(hide) sed -i.bak -e 's?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g' $@
- $(hide) sed -i.bak -e 's?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g' $@
+ $(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
+ $(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
+ $(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
+ $(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?g" $@
+ $(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
+ $(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
$(hide) rm -f $@.bak
ld_config_template :=
@@ -127,6 +177,7 @@
intermediates_dir :=
library_lists_dir :=
llndk_libraries_file :=
+llndk_moved_to_apex_libraries_file :=
vndksp_libraries_file :=
vndkcore_libraries_file :=
vndkprivate_libraries_file :=
@@ -136,3 +187,10 @@
llndk_libraries_list :=
vndksp_libraries_list :=
write-libs-to-file :=
+
+ifeq ($(my_vndk_use_core_variant),true)
+vndk_using_core_variant_libraries_file :=
+vndk_using_core_variant_libraries_list :=
+endif
+
+my_vndk_use_core_variant :=
diff --git a/demangle/.clang-format b/run-as/.clang-format
similarity index 100%
copy from demangle/.clang-format
copy to run-as/.clang-format
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index d005ecf..432c434 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -25,6 +25,9 @@
#include <sys/types.h>
#include <unistd.h>
+#include <string>
+#include <vector>
+
#include <libminijail.h>
#include <scoped_minijail.h>
@@ -67,32 +70,40 @@
return true; // Keep searching.
}
-static bool check_directory(const char* path, uid_t uid) {
+static void check_directory(const char* path, uid_t uid) {
struct stat st;
- if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) return false;
+ if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {
+ error(1, errno, "couldn't stat %s", path);
+ }
// /data/user/0 is a known safe symlink.
- if (strcmp("/data/user/0", path) == 0) return true;
+ if (strcmp("/data/user/0", path) == 0) return;
// Must be a real directory, not a symlink.
- if (!S_ISDIR(st.st_mode)) return false;
+ if (!S_ISDIR(st.st_mode)) {
+ error(1, 0, "%s not a directory: %o", path, st.st_mode);
+ }
// Must be owned by specific uid/gid.
- if (st.st_uid != uid || st.st_gid != uid) return false;
+ if (st.st_uid != uid || st.st_gid != uid) {
+ error(1, 0, "%s has wrong owner: %d/%d, not %d", path, st.st_uid, st.st_gid, uid);
+ }
// Must not be readable or writable by others.
- if ((st.st_mode & (S_IROTH|S_IWOTH)) != 0) return false;
-
- return true;
+ if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {
+ error(1, 0, "%s readable or writable by others: %o", path, st.st_mode);
+ }
}
// This function is used to check the data directory path for safety.
// We check that every sub-directory is owned by the 'system' user
// and exists and is not a symlink. We also check that the full directory
// path is properly owned by the user ID.
-static bool check_data_path(const char* data_path, uid_t uid) {
+static void check_data_path(const char* package_name, const char* data_path, uid_t uid) {
// The path should be absolute.
- if (data_path[0] != '/') return false;
+ if (data_path[0] != '/') {
+ error(1, 0, "%s data path not absolute: %s", package_name, data_path);
+ }
// Look for all sub-paths, we do that by finding
// directory separators in the input path and
@@ -107,26 +118,47 @@
if (data_path[nn+1] == '\0') break;
/* found a separator, check that data_path is not too long. */
- if (nn >= (int)(sizeof subpath)) return false;
+ if (nn >= (int)(sizeof subpath)) {
+ error(1, 0, "%s data path too long: %s", package_name, data_path);
+ }
/* reject any '..' subpath */
if (nn >= 3 &&
data_path[nn-3] == '/' &&
data_path[nn-2] == '.' &&
data_path[nn-1] == '.') {
- return false;
+ error(1, 0, "%s contains '..': %s", package_name, data_path);
}
/* copy to 'subpath', then check ownership */
memcpy(subpath, data_path, nn);
subpath[nn] = '\0';
- if (!check_directory(subpath, AID_SYSTEM)) return false;
+ check_directory(subpath, AID_SYSTEM);
}
// All sub-paths were checked, now verify that the full data
// directory is owned by the application uid.
- return check_directory(data_path, uid);
+ check_directory(data_path, uid);
+}
+
+std::vector<gid_t> get_supplementary_gids(uid_t userAppId) {
+ std::vector<gid_t> gids;
+ int size = getgroups(0, &gids[0]);
+ if (size < 0) {
+ error(1, errno, "getgroups failed");
+ }
+ gids.resize(size);
+ size = getgroups(size, &gids[0]);
+ if (size != static_cast<int>(gids.size())) {
+ error(1, errno, "getgroups failed");
+ }
+ // Profile guide compiled oat files (like /data/app/xxx/oat/arm64/base.odex) are not readable
+ // worldwide (DEXOPT_PUBLIC flag isn't set). To support reading them (needed by simpleperf for
+ // profiling), add shared app gid to supplementary groups.
+ gid_t shared_app_gid = userAppId % AID_USER_OFFSET - AID_APP_START + AID_SHARED_GID_START;
+ gids.push_back(shared_app_gid);
+ return gids;
}
int main(int argc, char* argv[]) {
@@ -144,7 +176,7 @@
// Some devices can disable running run-as, such as Chrome OS when running in
// non-developer mode.
if (android::base::GetBoolProperty("ro.boot.disable_runas", false)) {
- error(1, 0, "run-as is disabled from the kernel commandline");
+ error(1, 0, "run-as is disabled from the kernel commandline");
}
char* pkgname = argv[1];
@@ -167,6 +199,15 @@
if (!packagelist_parse(packagelist_parse_callback, &info)) {
error(1, errno, "packagelist_parse failed");
}
+
+ // Handle a multi-user data path
+ if (userId > 0) {
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+ }
+
if (info.uid == 0) {
error(1, 0, "unknown package: %s", pkgname);
}
@@ -191,21 +232,21 @@
}
// Check that the data directory path is valid.
- if (!check_data_path(info.data_dir, userAppId)) {
- error(1, 0, "package has corrupt installation: %s", pkgname);
- }
+ check_data_path(pkgname, info.data_dir, userAppId);
// Ensure that we change all real/effective/saved IDs at the
// same time to avoid nasty surprises.
uid_t uid = userAppId;
uid_t gid = userAppId;
+ std::vector<gid_t> supplementary_gids = get_supplementary_gids(userAppId);
ScopedMinijail j(minijail_new());
minijail_change_uid(j.get(), uid);
minijail_change_gid(j.get(), gid);
- minijail_keep_supplementary_gids(j.get());
+ minijail_set_supplementary_gids(j.get(), supplementary_gids.size(), supplementary_gids.data());
minijail_enter(j.get());
- if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
+ std::string seinfo = std::string(info.seinfo) + ":fromRunAs";
+ if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {
error(1, errno, "couldn't set SELinux security context");
}
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index dc36596..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -99,14 +100,21 @@
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
- mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
+ mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
+ bool use_esdfs) {
+ // Add new options at the end of the vector.
+ std::vector<std::string> new_opts_list;
+ if (multi_user) new_opts_list.push_back("multiuser,");
+ if (derive_gid) new_opts_list.push_back("derive_gid,");
+ if (default_normal) new_opts_list.push_back("default_normal,");
+ if (unshared_obb) new_opts_list.push_back("unshared_obb,");
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i <= new_opts_list.size(); ++i) {
std::string new_opts;
- if (multi_user && i < 3) new_opts += "multiuser,";
- if (derive_gid && i < 2) new_opts += "derive_gid,";
- if (default_normal && i < 1) new_opts += "default_normal,";
+ for (int j = 0; j < new_opts_list.size() - i; ++j) {
+ new_opts += new_opts_list[j];
+ }
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
@@ -142,13 +150,14 @@
return true;
}
-static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
- const std::string& dest_path, uid_t fsuid, gid_t fsgid,
- bool multi_user, userid_t userid, gid_t gid, mode_t mask,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+static bool sdcardfs_setup_secondary(const std::string& default_path,
+ const std::string& source_path, const std::string& dest_path,
+ uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,
+ gid_t gid, mode_t mask, bool derive_gid, bool default_normal,
+ bool unshared_obb, bool use_esdfs) {
if (use_esdfs) {
return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
- derive_gid, default_normal, use_esdfs);
+ derive_gid, default_normal, unshared_obb, use_esdfs);
} else {
return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
}
@@ -156,23 +165,28 @@
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
- bool derive_gid, bool default_normal, bool use_esdfs) {
+ bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
+ std::string dest_path_full = "/mnt/runtime/full/" + label;
umask(0);
if (multi_user) {
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
- default_normal, use_esdfs) ||
+ default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
- derive_gid, default_normal, use_esdfs)) {
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
+ !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+ multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
@@ -180,13 +194,17 @@
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
+ use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
- derive_gid, default_normal, use_esdfs) ||
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
- derive_gid, default_normal, use_esdfs)) {
+ derive_gid, default_normal, unshared_obb, use_esdfs) ||
+ !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
+ multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
+ default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
}
@@ -209,7 +227,8 @@
<< " -U: specify user ID that owns device"
<< " -m: source_path is multi-user"
<< " -w: runtime write mount has full write access"
- << " -P preserve owners on the lower file system";
+ << " -P: preserve owners on the lower file system"
+ << " -o: obb dir doesn't need to be shared between users";
return 1;
}
@@ -223,6 +242,7 @@
bool full_write = false;
bool derive_gid = false;
bool default_normal = false;
+ bool unshared_obb = false;
int i;
struct rlimit rlim;
int fs_version;
@@ -231,7 +251,7 @@
android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
int opt;
- while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:U:mwGio")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -254,8 +274,12 @@
case 'i':
default_normal = true;
break;
+ case 'o':
+ unshared_obb = true;
+ break;
case '?':
default:
+ LOG(ERROR) << "Unknown option: '" << opt << "'";
return usage();
}
}
@@ -297,6 +321,6 @@
}
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
- default_normal, !should_use_sdcardfs());
+ default_normal, unshared_obb, !should_use_sdcardfs());
return 1;
}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 2d4a26f..694b50e 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -10,14 +10,18 @@
phony {
name: "shell_and_utilities_system",
required: [
+ "auditctl",
"awk",
"bzip2",
- "grep",
+ "ldd",
"logwrapper",
+ "mini-keyctl",
"mkshrc",
"newfs_msdos",
"reboot",
"sh",
+ "simpleperf",
+ "simpleperf_app_runner",
"tcpdump",
"toolbox",
"toybox",
@@ -28,7 +32,6 @@
phony {
name: "shell_and_utilities_recovery",
required: [
- "grep.recovery",
"sh.recovery",
"toolbox.recovery",
"toybox.recovery",
@@ -40,7 +43,6 @@
name: "shell_and_utilities_vendor",
required: [
"awk_vendor",
- "grep_vendor",
"logwrapper_vendor",
"mkshrc_vendor",
"sh_vendor",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d8cf867..d391cc1 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -1,5 +1,4 @@
-Android's shell and utilities
-=============================
+# Android's shell and utilities
Since IceCreamSandwich Android has used
[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
@@ -34,8 +33,7 @@
full list for a release by running `toybox` directly.
-Android 2.3 (Gingerbread)
--------------------------
+## Android 2.3 (Gingerbread)
BSD: cat dd newfs\_msdos
@@ -46,8 +44,7 @@
umount uptime vmstat watchprops wipe
-Android 4.0 (IceCreamSandwich)
-------------------------------
+## Android 4.0 (IceCreamSandwich)
BSD: cat dd newfs\_msdos
@@ -58,8 +55,7 @@
touch umount uptime vmstat watchprops wipe
-Android 4.1-4.3 (JellyBean)
----------------------------
+## Android 4.1-4.3 (JellyBean)
BSD: cat cp dd du grep newfs\_msdos
@@ -71,8 +67,7 @@
sync top touch umount uptime vmstat watchprops wipe
-Android 4.4 (KitKat)
---------------------
+## Android 4.4 (KitKat)
BSD: cat cp dd du grep newfs\_msdos
@@ -84,8 +79,7 @@
stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
-Android 5.0 (Lollipop)
-----------------------
+## Android 5.0 (Lollipop)
BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
@@ -97,8 +91,7 @@
top touch umount uptime vmstat watchprops wipe
-Android 6.0 (Marshmallow)
--------------------------
+## Android 6.0 (Marshmallow)
BSD: dd du grep
@@ -118,8 +111,7 @@
vmstat wc which whoami xargs yes
-Android 7.0 (Nougat)
---------------------
+## Android 7.0 (Nougat)
BSD: dd grep
@@ -140,8 +132,7 @@
uptime usleep vmstat wc which whoami xargs xxd yes
-Android 8.0 (Oreo)
-------------------
+## Android 8.0 (Oreo)
BSD: dd grep
@@ -164,8 +155,8 @@
tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
vmstat wc which whoami xargs xxd yes zcat
-Android P
----------
+
+## Android 9.0 (Pie)
BSD: dd grep
@@ -190,8 +181,8 @@
umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
which whoami xargs xxd yes zcat
-Android Q
----------
+
+## Android Q
BSD: grep fsck\_msdos newfs\_msdos
@@ -201,17 +192,51 @@
toolbox: getevent getprop
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date dd df diff dirname
-dmesg dos2unix du echo env expand expr fallocate false file find flock
-fmt free getenforce groups gunzip gzip head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
-losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
-mkswap mktemp modinfo modprobe more mount mountpoint mv nc netcat netstat
-nice nl nohup nsenter od paste patch pgrep pidof pkill pmap printenv
-printf ps pwd readlink realpath renice restorecon rm rmdir rmmod runcon
-sed sendevent seq setenforce setprop setsid sha1sum sha224sum sha256sum
-sha384sum sha512sum sleep sort split start stat stop strings stty swapoff
-swapon sync sysctl tac tail tar taskset tee time timeout top touch tr
-true truncate tty ulimit umount uname uniq unix2dos unshare uptime usleep
-uudecode uuencode vmstat wc which whoami xargs xxd yes zcat
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee time timeout top touch tr traceroute traceroute6
+true truncate tty tunctl ulimit umount uname uniq unix2dos unlink
+unshare uptime usleep uudecode uuencode uuidgen vconfig vmstat watch
+wc which whoami xargs xxd yes zcat
+
+## Android R
+
+BSD: fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
+false fgrep file find flock fmt free freeramdisk fsfreeze getconf
+getenforce getfattr grep groups gunzip gzip head help hostname hwclock
+i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd insmod
+install ionice iorenice iotop kill killall ln load\_policy log logname
+losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe
+paste patch pgrep pidof ping ping6 pivot\_root pkill pmap printenv
+printf prlimit ps pwd pwdx readlink realpath renice restorecon rev
+rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr
+setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
+stat strings stty swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout top touch tr traceroute traceroute6 true truncate tty tunctl
+ulimit umount uname uniq unix2dos unlink unshare uptime usleep uudecode
+uuencode uuidgen vconfig vmstat watch wc which whoami xargs xxd yes zcat
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 089390a..1d934a2 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -343,20 +343,14 @@
if (mConfig.event_time_check_usec &&
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {
check_time = false;
- static time_t state_a;
- IF_ALOG_RATELIMIT_LOCAL(300, &state_a) {
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
- }
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
}
event();
if (mConfig.event_time_check_usec && check_time) {
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {
- static time_t state_b;
- IF_ALOG_RATELIMIT_LOCAL(300, &state_b) {
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
- }
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
return;
}
int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 8c0b3d1..ca2421b 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -87,12 +87,21 @@
day_start_tp += chrono::seconds(perf_history.day_start_sec());
nr_samples = perf_history.nr_samples();
+ if (nr_samples < recent_perf.size()) {
+ recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
+ }
+ size_t i = 0;
for (auto bw : perf_history.recent_perf()) {
- recent_perf.push_back(bw);
+ if (i < recent_perf.size()) {
+ recent_perf[i] = bw;
+ } else {
+ recent_perf.push_back(bw);
+ }
+ ++i;
}
nr_days = perf_history.nr_days();
- int i = 0;
+ i = 0;
for (auto bw : perf_history.daily_perf()) {
daily_perf[i++] = bw;
}
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index f08cf93..0cc603a 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,12 +1,10 @@
cc_defaults {
name: "toolbox_defaults",
-
cflags: [
"-Werror",
"-Wno-unused-parameter",
"-Wno-unused-const-variable",
"-D_FILE_OFFSET_BITS=64",
- "-DWITHOUT_NLS",
],
}
@@ -26,6 +24,8 @@
"toolbox.c",
"getevent.c",
"getprop.cpp",
+ "setprop.cpp",
+ "start.cpp",
],
generated_headers: [
"toolbox_input_labels",
@@ -38,6 +38,9 @@
symlinks: [
"getevent",
"getprop",
+ "setprop",
+ "start",
+ "stop",
],
}
@@ -53,46 +56,3 @@
vendor: true,
defaults: ["toolbox_binary_defaults"],
}
-
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
- name: "r",
- defaults: ["toolbox_defaults"],
- srcs: ["r.c"],
-}
-
-// We build BSD grep separately (but see http://b/111849261).
-cc_defaults {
- name: "grep_common",
- defaults: ["toolbox_defaults"],
- srcs: [
- "upstream-netbsd/usr.bin/grep/fastgrep.c",
- "upstream-netbsd/usr.bin/grep/file.c",
- "upstream-netbsd/usr.bin/grep/grep.c",
- "upstream-netbsd/usr.bin/grep/queue.c",
- "upstream-netbsd/usr.bin/grep/util.c",
- ],
- symlinks: [
- "egrep",
- "fgrep",
- ],
- sanitize: {
- integer_overflow: false,
- },
-}
-
-cc_binary {
- name: "grep",
- defaults: ["grep_common"],
- recovery_available: true,
-}
-
-// Build vendor grep.
-// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
-// when vendor_available is fully supported.
-cc_binary {
- name: "grep_vendor",
- stem: "grep",
- vendor: true,
- defaults: ["grep_common"],
-}
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index 39033ad..e2c77c3 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -530,6 +530,9 @@
const char *device = NULL;
const char *device_path = "/dev/input";
+ /* disable buffering on stdout */
+ setbuf(stdout, NULL);
+
opterr = 0;
do {
c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
- fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- if(argc < 2) return usage();
-
- int width = 4;
- if(!strcmp(argv[1], "-b")) {
- width = 1;
- argc--;
- argv++;
- } else if(!strcmp(argv[1], "-s")) {
- width = 2;
- argc--;
- argv++;
- }
-
- if(argc < 2) return usage();
- uintptr_t addr = strtoptr(argv[1], 0, 16);
-
- uintptr_t endaddr = 0;
- char* end = strchr(argv[1], '-');
- if (end)
- endaddr = strtoptr(end + 1, 0, 16);
-
- if (!endaddr)
- endaddr = addr + width - 1;
-
- if (endaddr <= addr) {
- fprintf(stderr, "end address <= start address\n");
- return -1;
- }
-
- bool set = false;
- uint32_t value = 0;
- if(argc > 2) {
- set = true;
- value = strtoul(argv[2], 0, 16);
- }
-
- int fd = open("/dev/mem", O_RDWR | O_SYNC);
- if(fd < 0) {
- fprintf(stderr,"cannot open /dev/mem\n");
- return -1;
- }
-
- off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
- size_t mmap_size = endaddr - mmap_start + 1;
- mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
- void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, mmap_start);
-
- if(page == MAP_FAILED){
- fprintf(stderr,"cannot mmap region\n");
- return -1;
- }
-
- while (addr <= endaddr) {
- switch(width){
- case 4: {
- uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
- break;
- }
- case 2: {
- uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
- break;
- }
- case 1: {
- uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
- break;
- }
- }
- addr += width;
- }
- return 0;
-}
diff --git a/toolbox/setprop.cpp b/toolbox/setprop.cpp
new file mode 100644
index 0000000..acf8c3e
--- /dev/null
+++ b/toolbox/setprop.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/system_properties.h>
+
+#include <iostream>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+using android::base::SetProperty;
+using android::base::StartsWith;
+
+extern "C" int setprop_main(int argc, char** argv) {
+ if (argc != 3) {
+ std::cout << "usage: setprop NAME VALUE\n"
+ "\n"
+ "Sets an Android system property."
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ auto name = std::string{argv[1]};
+ auto value = std::string{argv[2]};
+
+ // SetProperty() doesn't tell us why it failed, and actually can't recognize most failures, so
+ // we duplicate some of init's checks here to help the user.
+
+ if (name.front() == '.' || name.back() == '.') {
+ std::cerr << "Property names must not start or end with a '.'" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (name.find("..") != std::string::npos) {
+ std::cerr << "'..' is not allowed in a property name" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ for (const auto& c : name) {
+ if (!isalnum(c) && !strchr(":@_.-", c)) {
+ std::cerr << "Invalid character '" << c << "' in name '" << name << "'" << std::endl;
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) {
+ std::cerr << "Value '" << value << "' is too long, " << value.size()
+ << " bytes vs a max of " << PROP_VALUE_MAX << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+ std::cerr << "Value '" << value << "' is not a UTF8 encoded string" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (!SetProperty(name, value)) {
+ std::cerr << "Failed to set property '" << name << "' to '" << value
+ << "'.\nSee dmesg for error reason." << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/toolbox/start.cpp b/toolbox/start.cpp
new file mode 100644
index 0000000..b87ed15
--- /dev/null
+++ b/toolbox/start.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+
+using android::base::GetProperty;
+using android::base::SetProperty;
+using namespace std::literals;
+
+static void ControlService(bool start, const std::string& service) {
+ if (!android::base::SetProperty(start ? "ctl.start" : "ctl.stop", service)) {
+ std::cerr << "Unable to " << (start ? "start" : "stop") << " service '" << service
+ << "'\nSee dmesg for error reason." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void ControlDefaultServices(bool start) {
+ std::vector<std::string> services = {"netd", "surfaceflinger", "zygote"};
+
+ // Only start zygote_secondary if not single arch.
+ std::string zygote_configuration = GetProperty("ro.zygote", "");
+ if (zygote_configuration != "zygote32" && zygote_configuration != "zygote64") {
+ services.emplace_back("zygote_secondary");
+ }
+
+ if (start) {
+ for (const auto& service : services) {
+ ControlService(true, service);
+ }
+ } else {
+ for (auto it = services.crbegin(); it != services.crend(); ++it) {
+ ControlService(false, *it);
+ }
+ }
+}
+
+static int StartStop(int argc, char** argv, bool start) {
+ if (getuid()) {
+ std::cerr << "Must be root" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ if (argc == 1) {
+ ControlDefaultServices(start);
+ }
+
+ if (argc == 2 && argv[1] == "--help"s) {
+ std::cout << "usage: " << (start ? "start" : "stop")
+ << " [SERVICE...]\n"
+ "\n"
+ << (start ? "Starts" : "Stops")
+ << " the given system service, or netd/surfaceflinger/zygotes." << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ ControlService(start, argv[i]);
+ }
+ return EXIT_SUCCESS;
+}
+
+extern "C" int start_main(int argc, char** argv) {
+ return StartStop(argc, argv, true);
+}
+
+extern "C" int stop_main(int argc, char** argv) {
+ return StartStop(argc, argv, false);
+}
\ No newline at end of file
diff --git a/toolbox/tools.h b/toolbox/tools.h
index abeb3ef..9a7ebd2 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,3 +1,6 @@
TOOL(getevent)
TOOL(getprop)
+TOOL(setprop)
+TOOL(start)
+TOOL(stop)
TOOL(toolbox)
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
deleted file mode 100644
index 2fcd864..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */
-/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * XXX: This file is a speed up for grep to cover the defects of the
- * regex library. These optimizations should practically be implemented
- * there keeping this code clean. This is a future TODO, but for the
- * meantime, we need to use this workaround.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
-
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t);
-static inline void grep_revstr(unsigned char *, int);
-
-void
-fgrepcomp(fastgrep_t *fg, const char *pat)
-{
- unsigned int i;
-
- /* Initialize. */
- fg->len = strlen(pat);
- fg->bol = false;
- fg->eol = false;
- fg->reversed = false;
-
- fg->pattern = (unsigned char *)grep_strdup(pat);
-
- /* Preprocess pattern. */
- for (i = 0; i <= UCHAR_MAX; i++)
- fg->qsBc[i] = fg->len;
- for (i = 1; i < fg->len; i++)
- fg->qsBc[fg->pattern[i]] = fg->len - i;
-}
-
-/*
- * Returns: -1 on failure, 0 on success
- */
-int
-fastcomp(fastgrep_t *fg, const char *pat)
-{
- unsigned int i;
- int firstHalfDot = -1;
- int firstLastHalfDot = -1;
- int hasDot = 0;
- int lastHalfDot = 0;
- int shiftPatternLen;
-
- /* Initialize. */
- fg->len = strlen(pat);
- fg->bol = false;
- fg->eol = false;
- fg->reversed = false;
- fg->word = wflag;
-
- /* Remove end-of-line character ('$'). */
- if (fg->len > 0 && pat[fg->len - 1] == '$') {
- fg->eol = true;
- fg->len--;
- }
-
- /* Remove beginning-of-line character ('^'). */
- if (pat[0] == '^') {
- fg->bol = true;
- fg->len--;
- pat++;
- }
-
- if (fg->len >= 14 &&
- memcmp(pat, "[[:<:]]", 7) == 0 &&
- memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
- fg->len -= 14;
- pat += 7;
- /* Word boundary is handled separately in util.c */
- fg->word = true;
- }
-
- /*
- * pat has been adjusted earlier to not include '^', '$' or
- * the word match character classes at the beginning and ending
- * of the string respectively.
- */
- fg->pattern = grep_malloc(fg->len + 1);
- memcpy(fg->pattern, pat, fg->len);
- fg->pattern[fg->len] = '\0';
-
- /* Look for ways to cheat...er...avoid the full regex engine. */
- for (i = 0; i < fg->len; i++) {
- /* Can still cheat? */
- if (fg->pattern[i] == '.') {
- hasDot = i;
- if (i < fg->len / 2) {
- if (firstHalfDot < 0)
- /* Closest dot to the beginning */
- firstHalfDot = i;
- } else {
- /* Closest dot to the end of the pattern. */
- lastHalfDot = i;
- if (firstLastHalfDot < 0)
- firstLastHalfDot = i;
- }
- } else {
- /* Free memory and let others know this is empty. */
- free(fg->pattern);
- fg->pattern = NULL;
- return (-1);
- }
- }
-
- /*
- * Determine if a reverse search would be faster based on the placement
- * of the dots.
- */
- if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
- ((lastHalfDot) && ((firstHalfDot < 0) ||
- ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
- !oflag && !color) {
- fg->reversed = true;
- hasDot = fg->len - (firstHalfDot < 0 ?
- firstLastHalfDot : firstHalfDot) - 1;
- grep_revstr(fg->pattern, fg->len);
- }
-
- /*
- * Normal Quick Search would require a shift based on the position the
- * next character after the comparison is within the pattern. With
- * wildcards, the position of the last dot effects the maximum shift
- * distance.
- * The closer to the end the wild card is the slower the search. A
- * reverse version of this algorithm would be useful for wildcards near
- * the end of the string.
- *
- * Examples:
- * Pattern Max shift
- * ------- ---------
- * this 5
- * .his 4
- * t.is 3
- * th.s 2
- * thi. 1
- */
-
- /* Adjust the shift based on location of the last dot ('.'). */
- shiftPatternLen = fg->len - hasDot;
-
- /* Preprocess pattern. */
- for (i = 0; i <= (signed)UCHAR_MAX; i++)
- fg->qsBc[i] = shiftPatternLen;
- for (i = hasDot + 1; i < fg->len; i++) {
- fg->qsBc[fg->pattern[i]] = fg->len - i;
- }
-
- /*
- * Put pattern back to normal after pre-processing to allow for easy
- * comparisons later.
- */
- if (fg->reversed)
- grep_revstr(fg->pattern, fg->len);
-
- return (0);
-}
-
-int
-grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
-{
- unsigned int j;
- int ret = REG_NOMATCH;
-
- if (pmatch->rm_so == (ssize_t)len)
- return (ret);
-
- if (fg->bol && pmatch->rm_so != 0) {
- pmatch->rm_so = len;
- pmatch->rm_eo = len;
- return (ret);
- }
-
- /* No point in going farther if we do not have enough data. */
- if (len < fg->len)
- return (ret);
-
- /* Only try once at the beginning or ending of the line. */
- if (fg->bol || fg->eol) {
- /* Simple text comparison. */
- /* Verify data is >= pattern length before searching on it. */
- if (len >= fg->len) {
- /* Determine where in data to start search at. */
- j = fg->eol ? len - fg->len : 0;
- if (!((fg->bol && fg->eol) && (len != fg->len)))
- if (grep_cmp(fg->pattern, data + j,
- fg->len) == -1) {
- pmatch->rm_so = j;
- pmatch->rm_eo = j + fg->len;
- ret = 0;
- }
- }
- } else if (fg->reversed) {
- /* Quick Search algorithm. */
- j = len;
- do {
- if (grep_cmp(fg->pattern, data + j - fg->len,
- fg->len) == -1) {
- pmatch->rm_so = j - fg->len;
- pmatch->rm_eo = j;
- ret = 0;
- break;
- }
- /* Shift if within bounds, otherwise, we are done. */
- if (j == fg->len)
- break;
- j -= fg->qsBc[data[j - fg->len - 1]];
- } while (j >= fg->len);
- } else {
- /* Quick Search algorithm. */
- j = pmatch->rm_so;
- do {
- if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
- pmatch->rm_so = j;
- pmatch->rm_eo = j + fg->len;
- ret = 0;
- break;
- }
-
- /* Shift if within bounds, otherwise, we are done. */
- if (j + fg->len == len)
- break;
- else
- j += fg->qsBc[data[j + fg->len]];
- } while (j <= (len - fg->len));
- }
-
- return (ret);
-}
-
-/*
- * Returns: i >= 0 on failure (position that it failed)
- * -1 on success
- */
-static inline int
-grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
-{
- size_t size;
- wchar_t *wdata, *wpat;
- unsigned int i;
-
- if (iflag) {
- if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
- ((size_t) - 1))
- return (-1);
-
- wdata = grep_malloc(size * sizeof(wint_t));
-
- if (mbstowcs(wdata, (const char *)data, size) ==
- ((size_t) - 1))
- return (-1);
-
- if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
- ((size_t) - 1))
- return (-1);
-
- wpat = grep_malloc(size * sizeof(wint_t));
-
- if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
- return (-1);
- for (i = 0; i < len; i++) {
- if ((towlower(wpat[i]) == towlower(wdata[i])) ||
- ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
- continue;
- free(wpat);
- free(wdata);
- return (i);
- }
- } else {
- for (i = 0; i < len; i++) {
- if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
- pat[i] == '.'))
- continue;
- return (i);
- }
- }
- return (-1);
-}
-
-static inline void
-grep_revstr(unsigned char *str, int len)
-{
- int i;
- char c;
-
- for (i = 0; i < len / 2; i++) {
- c = str[i];
- str[i] = str[len - i - 1];
- str[len - i - 1] = c;
- }
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
deleted file mode 100644
index cf4a0fa..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
-/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
-/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#include "grep.h"
-
-#define MAXBUFSIZ (32 * 1024)
-#define LNBUFBUMP 80
-
-#ifndef __ANDROID__
-static gzFile gzbufdesc;
-static BZFILE* bzbufdesc;
-#endif
-
-static unsigned char buffer[MAXBUFSIZ];
-static unsigned char *bufpos;
-static size_t bufrem;
-
-static unsigned char *lnbuf;
-static size_t lnbuflen;
-
-static inline int
-grep_refill(struct file *f)
-{
- ssize_t nr;
-#ifndef __ANDROID__
- int bzerr;
-#endif
-
- bufpos = buffer;
- bufrem = 0;
-
-#ifndef __ANDROID__
- if (filebehave == FILE_GZIP)
- nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
- else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
- nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
- switch (bzerr) {
- case BZ_OK:
- case BZ_STREAM_END:
- /* No problem, nr will be okay */
- break;
- case BZ_DATA_ERROR_MAGIC:
- /*
- * As opposed to gzread(), which simply returns the
- * plain file data, if it is not in the correct
- * compressed format, BZ2_bzRead() instead aborts.
- *
- * So, just restart at the beginning of the file again,
- * and use plain reads from now on.
- */
- BZ2_bzReadClose(&bzerr, bzbufdesc);
- bzbufdesc = NULL;
- if (lseek(f->fd, 0, SEEK_SET) == -1)
- return (-1);
- nr = read(f->fd, buffer, MAXBUFSIZ);
- break;
- default:
- /* Make sure we exit with an error */
- nr = -1;
- }
- } else
-#endif
- nr = read(f->fd, buffer, MAXBUFSIZ);
-
- if (nr < 0)
- return (-1);
-
- bufrem = nr;
- return (0);
-}
-
-static inline int
-grep_lnbufgrow(size_t newlen)
-{
-
- if (lnbuflen < newlen) {
- lnbuf = grep_realloc(lnbuf, newlen);
- lnbuflen = newlen;
- }
-
- return (0);
-}
-
-char *
-grep_fgetln(struct file *f, size_t *lenp)
-{
- unsigned char *p;
- char *ret;
- size_t len;
- size_t off;
- ptrdiff_t diff;
-
- /* Fill the buffer, if necessary */
- if (bufrem == 0 && grep_refill(f) != 0)
- goto error;
-
- if (bufrem == 0) {
- /* Return zero length to indicate EOF */
- *lenp = 0;
- return ((char *)bufpos);
- }
-
- /* Look for a newline in the remaining part of the buffer */
- if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
- ++p; /* advance over newline */
- ret = (char *)bufpos;
- len = p - bufpos;
- bufrem -= len;
- bufpos = p;
- *lenp = len;
- return (ret);
- }
-
- /* We have to copy the current buffered data to the line buffer */
- for (len = bufrem, off = 0; ; len += bufrem) {
- /* Make sure there is room for more data */
- if (grep_lnbufgrow(len + LNBUFBUMP))
- goto error;
- memcpy(lnbuf + off, bufpos, len - off);
- off = len;
- if (grep_refill(f) != 0)
- goto error;
- if (bufrem == 0)
- /* EOF: return partial line */
- break;
- if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
- continue;
- /* got it: finish up the line (like code above) */
- ++p;
- diff = p - bufpos;
- len += diff;
- if (grep_lnbufgrow(len))
- goto error;
- memcpy(lnbuf + off, bufpos, diff);
- bufrem -= diff;
- bufpos = p;
- break;
- }
- *lenp = len;
- return ((char *)lnbuf);
-
-error:
- *lenp = 0;
- return (NULL);
-}
-
-static inline struct file *
-grep_file_init(struct file *f)
-{
-
-#ifndef __ANDROID__
- if (filebehave == FILE_GZIP &&
- (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
- goto error;
-
- if (filebehave == FILE_BZIP &&
- (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
- goto error;
-#endif
-
- /* Fill read buffer, also catches errors early */
- if (grep_refill(f) != 0)
- goto error;
-
- /* Check for binary stuff, if necessary */
- if (!nulldataflag && binbehave != BINFILE_TEXT &&
- memchr(bufpos, '\0', bufrem) != NULL)
- f->binary = true;
-
- return (f);
-error:
- close(f->fd);
- free(f);
- return (NULL);
-}
-
-/*
- * Opens a file for processing.
- */
-struct file *
-grep_open(const char *path)
-{
- struct file *f;
-
- f = grep_malloc(sizeof *f);
- memset(f, 0, sizeof *f);
- if (path == NULL) {
- /* Processing stdin implies --line-buffered. */
- lbflag = true;
- f->fd = STDIN_FILENO;
- } else if ((f->fd = open(path, O_RDONLY)) == -1) {
- free(f);
- return (NULL);
- }
-
- return (grep_file_init(f));
-}
-
-/*
- * Closes a file.
- */
-void
-grep_close(struct file *f)
-{
-
- close(f->fd);
-
- /* Reset read buffer and line buffer */
- bufpos = buffer;
- bufrem = 0;
-
- free(lnbuf);
- lnbuf = NULL;
- lnbuflen = 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
deleted file mode 100644
index 1ea6ed3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
-/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
-/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <getopt.h>
-#include <limits.h>
-#include <libgen.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "grep.h"
-
-#ifndef WITHOUT_NLS
-#include <nl_types.h>
-nl_catd catalog;
-#endif
-
-/*
- * Default messags to use when NLS is disabled or no catalogue
- * is found.
- */
-const char *errstr[] = {
- "",
-/* 1*/ "(standard input)",
-/* 2*/ "cannot read bzip2 compressed file",
-/* 3*/ "unknown %s option",
-/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
-/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
-/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
-/* 7*/ "\t[pattern] [file ...]\n",
-/* 8*/ "Binary file %s matches\n",
-/* 9*/ "%s (BSD grep) %s\n",
-};
-
-/* Flags passed to regcomp() and regexec() */
-int cflags = 0;
-int eflags = REG_STARTEND;
-
-/* Searching patterns */
-unsigned int patterns, pattern_sz;
-char **pattern;
-regex_t *r_pattern;
-fastgrep_t *fg_pattern;
-
-/* Filename exclusion/inclusion patterns */
-unsigned int fpatterns, fpattern_sz;
-unsigned int dpatterns, dpattern_sz;
-struct epat *dpattern, *fpattern;
-
-/* For regex errors */
-char re_error[RE_ERROR_BUF + 1];
-
-/* Command-line flags */
-unsigned long long Aflag; /* -A x: print x lines trailing each match */
-unsigned long long Bflag; /* -B x: print x lines leading each match */
-bool Hflag; /* -H: always print file name */
-bool Lflag; /* -L: only show names of files with no matches */
-bool bflag; /* -b: show block numbers for each match */
-bool cflag; /* -c: only show a count of matching lines */
-bool hflag; /* -h: don't print filename headers */
-bool iflag; /* -i: ignore case */
-bool lflag; /* -l: only show names of files with matches */
-bool mflag; /* -m x: stop reading the files after x matches */
-unsigned long long mcount; /* count for -m */
-bool nflag; /* -n: show line numbers in front of matching lines */
-bool oflag; /* -o: print only matching part */
-bool qflag; /* -q: quiet mode (don't output anything) */
-bool sflag; /* -s: silent mode (ignore errors) */
-bool vflag; /* -v: only show non-matching lines */
-bool wflag; /* -w: pattern must start and end on word boundaries */
-bool xflag; /* -x: pattern must match entire line */
-bool lbflag; /* --line-buffered */
-bool nullflag; /* --null */
-bool nulldataflag; /* --null-data */
-unsigned char line_sep = '\n'; /* 0 for --null-data */
-char *label; /* --label */
-const char *color; /* --color */
-int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
-int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
-int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
-int devbehave = DEV_READ; /* -D: handling of devices */
-int dirbehave = DIR_READ; /* -dRr: handling of directories */
-int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
-
-bool dexclude, dinclude; /* --exclude-dir and --include-dir */
-bool fexclude, finclude; /* --exclude and --include */
-
-enum {
- BIN_OPT = CHAR_MAX + 1,
- COLOR_OPT,
- DECOMPRESS_OPT,
- HELP_OPT,
- MMAP_OPT,
- LINEBUF_OPT,
- LABEL_OPT,
- R_EXCLUDE_OPT,
- R_INCLUDE_OPT,
- R_DEXCLUDE_OPT,
- R_DINCLUDE_OPT
-};
-
-static inline const char *init_color(const char *);
-
-/* Housekeeping */
-int tail; /* lines left to print */
-bool notfound; /* file not found */
-
-extern char *__progname;
-
-/*
- * Prints usage information and returns 2.
- */
-__dead static void
-usage(void)
-{
- fprintf(stderr, getstr(4), __progname);
- fprintf(stderr, "%s", getstr(5));
- fprintf(stderr, "%s", getstr(6));
- fprintf(stderr, "%s", getstr(7));
- exit(2);
-}
-
-static const char optstr[] =
- "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
-
-struct option long_options[] =
-{
- {"binary-files", required_argument, NULL, BIN_OPT},
- {"decompress", no_argument, NULL, DECOMPRESS_OPT},
- {"help", no_argument, NULL, HELP_OPT},
- {"mmap", no_argument, NULL, MMAP_OPT},
- {"line-buffered", no_argument, NULL, LINEBUF_OPT},
- {"label", required_argument, NULL, LABEL_OPT},
- {"color", optional_argument, NULL, COLOR_OPT},
- {"colour", optional_argument, NULL, COLOR_OPT},
- {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
- {"include", required_argument, NULL, R_INCLUDE_OPT},
- {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
- {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
- {"after-context", required_argument, NULL, 'A'},
- {"text", no_argument, NULL, 'a'},
- {"before-context", required_argument, NULL, 'B'},
- {"byte-offset", no_argument, NULL, 'b'},
- {"context", optional_argument, NULL, 'C'},
- {"count", no_argument, NULL, 'c'},
- {"devices", required_argument, NULL, 'D'},
- {"directories", required_argument, NULL, 'd'},
- {"extended-regexp", no_argument, NULL, 'E'},
- {"regexp", required_argument, NULL, 'e'},
- {"fixed-strings", no_argument, NULL, 'F'},
- {"file", required_argument, NULL, 'f'},
- {"basic-regexp", no_argument, NULL, 'G'},
- {"no-filename", no_argument, NULL, 'h'},
- {"with-filename", no_argument, NULL, 'H'},
- {"ignore-case", no_argument, NULL, 'i'},
- {"bz2decompress", no_argument, NULL, 'J'},
- {"files-with-matches", no_argument, NULL, 'l'},
- {"files-without-match", no_argument, NULL, 'L'},
- {"max-count", required_argument, NULL, 'm'},
- {"line-number", no_argument, NULL, 'n'},
- {"only-matching", no_argument, NULL, 'o'},
- {"quiet", no_argument, NULL, 'q'},
- {"silent", no_argument, NULL, 'q'},
- {"recursive", no_argument, NULL, 'r'},
- {"no-messages", no_argument, NULL, 's'},
- {"binary", no_argument, NULL, 'U'},
- {"unix-byte-offsets", no_argument, NULL, 'u'},
- {"invert-match", no_argument, NULL, 'v'},
- {"version", no_argument, NULL, 'V'},
- {"word-regexp", no_argument, NULL, 'w'},
- {"line-regexp", no_argument, NULL, 'x'},
- {"null", no_argument, NULL, 'Z'},
- {"null-data", no_argument, NULL, 'z'},
- {NULL, no_argument, NULL, 0}
-};
-
-/*
- * Adds a searching pattern to the internal array.
- */
-static void
-add_pattern(char *pat, size_t len)
-{
-
- /* TODO: Check for empty patterns and shortcut */
-
- /* Increase size if necessary */
- if (patterns == pattern_sz) {
- pattern_sz *= 2;
- pattern = grep_realloc(pattern, ++pattern_sz *
- sizeof(*pattern));
- }
- if (len > 0 && pat[len - 1] == '\n')
- --len;
- /* pat may not be NUL-terminated */
- pattern[patterns] = grep_malloc(len + 1);
- memcpy(pattern[patterns], pat, len);
- pattern[patterns][len] = '\0';
- ++patterns;
-}
-
-/*
- * Adds a file include/exclude pattern to the internal array.
- */
-static void
-add_fpattern(const char *pat, int mode)
-{
-
- /* Increase size if necessary */
- if (fpatterns == fpattern_sz) {
- fpattern_sz *= 2;
- fpattern = grep_realloc(fpattern, ++fpattern_sz *
- sizeof(struct epat));
- }
- fpattern[fpatterns].pat = grep_strdup(pat);
- fpattern[fpatterns].mode = mode;
- ++fpatterns;
-}
-
-/*
- * Adds a directory include/exclude pattern to the internal array.
- */
-static void
-add_dpattern(const char *pat, int mode)
-{
-
- /* Increase size if necessary */
- if (dpatterns == dpattern_sz) {
- dpattern_sz *= 2;
- dpattern = grep_realloc(dpattern, ++dpattern_sz *
- sizeof(struct epat));
- }
- dpattern[dpatterns].pat = grep_strdup(pat);
- dpattern[dpatterns].mode = mode;
- ++dpatterns;
-}
-
-/*
- * Reads searching patterns from a file and adds them with add_pattern().
- */
-static void
-read_patterns(const char *fn)
-{
- FILE *f;
- char *line;
- size_t len;
- ssize_t rlen;
-
- if ((f = fopen(fn, "r")) == NULL)
- err(2, "%s", fn);
- line = NULL;
- len = 0;
- while ((rlen = getline(&line, &len, f)) != -1)
- add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
- free(line);
- if (ferror(f))
- err(2, "%s", fn);
- fclose(f);
-}
-
-static inline const char *
-init_color(const char *d)
-{
- char *c;
-
- c = getenv("GREP_COLOR");
- return (c != NULL ? c : d);
-}
-
-int
-main(int argc, char *argv[])
-{
- char **aargv, **eargv, *eopts;
- char *ep;
- unsigned long long l;
- unsigned int aargc, eargc, i, j;
- int c, lastc, needpattern, newarg, prevoptind;
-
- setlocale(LC_ALL, "");
-
-#ifndef WITHOUT_NLS
- catalog = catopen("grep", NL_CAT_LOCALE);
-#endif
-
- /* Check what is the program name of the binary. In this
- way we can have all the funcionalities in one binary
- without the need of scripting and using ugly hacks. */
- switch (__progname[0]) {
- case 'e':
- grepbehave = GREP_EXTENDED;
- break;
- case 'f':
- grepbehave = GREP_FIXED;
- break;
- case 'g':
- grepbehave = GREP_BASIC;
- break;
- case 'z':
- filebehave = FILE_GZIP;
- switch(__progname[1]) {
- case 'e':
- grepbehave = GREP_EXTENDED;
- break;
- case 'f':
- grepbehave = GREP_FIXED;
- break;
- case 'g':
- grepbehave = GREP_BASIC;
- break;
- }
- break;
- }
-
- lastc = '\0';
- newarg = 1;
- prevoptind = 1;
- needpattern = 1;
-
- eopts = getenv("GREP_OPTIONS");
-
- /* support for extra arguments in GREP_OPTIONS */
- eargc = 0;
- if (eopts != NULL) {
- char *str;
-
- /* make an estimation of how many extra arguments we have */
- for (j = 0; j < strlen(eopts); j++)
- if (eopts[j] == ' ')
- eargc++;
-
- eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
-
- eargc = 0;
- /* parse extra arguments */
- while ((str = strsep(&eopts, " ")) != NULL)
- eargv[eargc++] = grep_strdup(str);
-
- aargv = (char **)grep_calloc(eargc + argc + 1,
- sizeof(char *));
-
- aargv[0] = argv[0];
- for (i = 0; i < eargc; i++)
- aargv[i + 1] = eargv[i];
- for (j = 1; j < (unsigned int)argc; j++, i++)
- aargv[i + 1] = argv[j];
-
- aargc = eargc + argc;
- } else {
- aargv = argv;
- aargc = argc;
- }
-
- while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
- -1)) {
- switch (c) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (newarg || !isdigit(lastc))
- Aflag = 0;
- else if (Aflag > LLONG_MAX / 10) {
- errno = ERANGE;
- err(2, NULL);
- }
- Aflag = Bflag = (Aflag * 10) + (c - '0');
- break;
- case 'C':
- if (optarg == NULL) {
- Aflag = Bflag = 2;
- break;
- }
- /* FALLTHROUGH */
- case 'A':
- /* FALLTHROUGH */
- case 'B':
- errno = 0;
- l = strtoull(optarg, &ep, 10);
- if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
- ((errno == EINVAL) && (l == 0)))
- err(2, NULL);
- else if (ep[0] != '\0') {
- errno = EINVAL;
- err(2, NULL);
- }
- if (c == 'A')
- Aflag = l;
- else if (c == 'B')
- Bflag = l;
- else
- Aflag = Bflag = l;
- break;
- case 'a':
- binbehave = BINFILE_TEXT;
- break;
- case 'b':
- bflag = true;
- break;
- case 'c':
- cflag = true;
- break;
- case 'D':
- if (strcasecmp(optarg, "skip") == 0)
- devbehave = DEV_SKIP;
- else if (strcasecmp(optarg, "read") == 0)
- devbehave = DEV_READ;
- else
- errx(2, getstr(3), "--devices");
- break;
- case 'd':
- if (strcasecmp("recurse", optarg) == 0) {
- Hflag = true;
- dirbehave = DIR_RECURSE;
- } else if (strcasecmp("skip", optarg) == 0)
- dirbehave = DIR_SKIP;
- else if (strcasecmp("read", optarg) == 0)
- dirbehave = DIR_READ;
- else
- errx(2, getstr(3), "--directories");
- break;
- case 'E':
- grepbehave = GREP_EXTENDED;
- break;
- case 'e':
- add_pattern(optarg, strlen(optarg));
- needpattern = 0;
- break;
- case 'F':
- grepbehave = GREP_FIXED;
- break;
- case 'f':
- read_patterns(optarg);
- needpattern = 0;
- break;
- case 'G':
- grepbehave = GREP_BASIC;
- break;
- case 'H':
- Hflag = true;
- break;
- case 'h':
- Hflag = false;
- hflag = true;
- break;
- case 'I':
- binbehave = BINFILE_SKIP;
- break;
- case 'i':
- case 'y':
- iflag = true;
- cflags |= REG_ICASE;
- break;
- case 'J':
- filebehave = FILE_BZIP;
- break;
- case 'L':
- lflag = false;
- Lflag = true;
- break;
- case 'l':
- Lflag = false;
- lflag = true;
- break;
- case 'm':
- mflag = true;
- errno = 0;
- mcount = strtoull(optarg, &ep, 10);
- if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
- ((errno == EINVAL) && (mcount == 0)))
- err(2, NULL);
- else if (ep[0] != '\0') {
- errno = EINVAL;
- err(2, NULL);
- }
- break;
- case 'n':
- nflag = true;
- break;
- case 'O':
- linkbehave = LINK_EXPLICIT;
- break;
- case 'o':
- oflag = true;
- break;
- case 'p':
- linkbehave = LINK_SKIP;
- break;
- case 'q':
- qflag = true;
- break;
- case 'S':
- linkbehave = LINK_READ;
- break;
- case 'R':
- case 'r':
- dirbehave = DIR_RECURSE;
- Hflag = true;
- break;
- case 's':
- sflag = true;
- break;
- case 'U':
- binbehave = BINFILE_BIN;
- break;
- case 'u':
- case MMAP_OPT:
- /* noop, compatibility */
- break;
- case 'V':
- printf(getstr(9), __progname, VERSION);
- exit(0);
- case 'v':
- vflag = true;
- break;
- case 'w':
- wflag = true;
- break;
- case 'x':
- xflag = true;
- break;
- case 'Z':
- nullflag = true;
- break;
- case 'z':
- nulldataflag = true;
- line_sep = '\0';
- break;
- case BIN_OPT:
- if (strcasecmp("binary", optarg) == 0)
- binbehave = BINFILE_BIN;
- else if (strcasecmp("without-match", optarg) == 0)
- binbehave = BINFILE_SKIP;
- else if (strcasecmp("text", optarg) == 0)
- binbehave = BINFILE_TEXT;
- else
- errx(2, getstr(3), "--binary-files");
- break;
- case COLOR_OPT:
- color = NULL;
- if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
- strcasecmp("tty", optarg) == 0 ||
- strcasecmp("if-tty", optarg) == 0) {
- char *term;
-
- term = getenv("TERM");
- if (isatty(STDOUT_FILENO) && term != NULL &&
- strcasecmp(term, "dumb") != 0)
- color = init_color("01;31");
- } else if (strcasecmp("always", optarg) == 0 ||
- strcasecmp("yes", optarg) == 0 ||
- strcasecmp("force", optarg) == 0) {
- color = init_color("01;31");
- } else if (strcasecmp("never", optarg) != 0 &&
- strcasecmp("none", optarg) != 0 &&
- strcasecmp("no", optarg) != 0)
- errx(2, getstr(3), "--color");
- break;
- case DECOMPRESS_OPT:
- filebehave = FILE_GZIP;
- break;
- case LABEL_OPT:
- label = optarg;
- break;
- case LINEBUF_OPT:
- lbflag = true;
- break;
- case R_INCLUDE_OPT:
- finclude = true;
- add_fpattern(optarg, INCL_PAT);
- break;
- case R_EXCLUDE_OPT:
- fexclude = true;
- add_fpattern(optarg, EXCL_PAT);
- break;
- case R_DINCLUDE_OPT:
- dinclude = true;
- add_dpattern(optarg, INCL_PAT);
- break;
- case R_DEXCLUDE_OPT:
- dexclude = true;
- add_dpattern(optarg, EXCL_PAT);
- break;
- case HELP_OPT:
- default:
- usage();
- }
- lastc = c;
- newarg = optind != prevoptind;
- prevoptind = optind;
- }
- aargc -= optind;
- aargv += optind;
-
- /* Fail if we don't have any pattern */
- if (aargc == 0 && needpattern)
- usage();
-
- /* Process patterns from command line */
- if (aargc != 0 && needpattern) {
- add_pattern(*aargv, strlen(*aargv));
- --aargc;
- ++aargv;
- }
-
- switch (grepbehave) {
- case GREP_FIXED:
- case GREP_BASIC:
- break;
- case GREP_EXTENDED:
- cflags |= REG_EXTENDED;
- break;
- default:
- /* NOTREACHED */
- usage();
- }
-
- fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
- r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
-/*
- * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
- * Optimizations should be done there.
- */
- /* Check if cheating is allowed (always is for fgrep). */
- if (grepbehave == GREP_FIXED) {
- for (i = 0; i < patterns; ++i)
- fgrepcomp(&fg_pattern[i], pattern[i]);
- } else {
- for (i = 0; i < patterns; ++i) {
- if (fastcomp(&fg_pattern[i], pattern[i])) {
- /* Fall back to full regex library */
- c = regcomp(&r_pattern[i], pattern[i], cflags);
- if (c != 0) {
- regerror(c, &r_pattern[i], re_error,
- RE_ERROR_BUF);
- errx(2, "%s", re_error);
- }
- }
- }
- }
-
- if (lbflag)
- setlinebuf(stdout);
-
- if ((aargc == 0 || aargc == 1) && !Hflag)
- hflag = true;
-
- if (aargc == 0)
- exit(!procfile("-"));
-
- if (dirbehave == DIR_RECURSE)
- c = grep_tree(aargv);
- else
- for (c = 0; aargc--; ++aargv) {
- if ((finclude || fexclude) && !file_matching(*aargv))
- continue;
- c+= procfile(*aargv);
- }
-
-#ifndef WITHOUT_NLS
- catclose(catalog);
-#endif
-
- /* Find out the correct return value according to the
- results and the command line option. */
- exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
deleted file mode 100644
index fa2a3e3..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
-/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
-/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef __ANDROID__
-#include <bzlib.h>
-#endif
-#include <limits.h>
-#include <regex.h>
-#include <stdbool.h>
-#include <stdio.h>
-#ifndef __ANDROID__
-#include <zlib.h>
-#endif
-
-#ifdef WITHOUT_NLS
-#define getstr(n) errstr[n]
-#else
-#include <nl_types.h>
-
-extern nl_catd catalog;
-#define getstr(n) catgets(catalog, 1, n, errstr[n])
-#endif
-
-extern const char *errstr[];
-
-#define VERSION "2.5.1-FreeBSD"
-
-#define GREP_FIXED 0
-#define GREP_BASIC 1
-#define GREP_EXTENDED 2
-
-#define BINFILE_BIN 0
-#define BINFILE_SKIP 1
-#define BINFILE_TEXT 2
-
-#define FILE_STDIO 0
-#define FILE_GZIP 1
-#define FILE_BZIP 2
-
-#define DIR_READ 0
-#define DIR_SKIP 1
-#define DIR_RECURSE 2
-
-#define DEV_READ 0
-#define DEV_SKIP 1
-
-#define LINK_READ 0
-#define LINK_EXPLICIT 1
-#define LINK_SKIP 2
-
-#define EXCL_PAT 0
-#define INCL_PAT 1
-
-#define MAX_LINE_MATCHES 32
-
-struct file {
- int fd;
- bool binary;
-};
-
-struct str {
- off_t off;
- size_t len;
- char *dat;
- char *file;
- int line_no;
-};
-
-struct epat {
- char *pat;
- int mode;
-};
-
-typedef struct {
- size_t len;
- unsigned char *pattern;
- int qsBc[UCHAR_MAX + 1];
- /* flags */
- bool bol;
- bool eol;
- bool reversed;
- bool word;
-} fastgrep_t;
-
-/* Flags passed to regcomp() and regexec() */
-extern int cflags, eflags;
-
-/* Command line flags */
-extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
- bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
- qflag, sflag, vflag, wflag, xflag;
-extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
-extern unsigned char line_sep;
-extern unsigned long long Aflag, Bflag, mcount;
-extern char *label;
-extern const char *color;
-extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
-
-extern bool notfound;
-extern int tail;
-extern unsigned int dpatterns, fpatterns, patterns;
-extern char **pattern;
-extern struct epat *dpattern, *fpattern;
-extern regex_t *er_pattern, *r_pattern;
-extern fastgrep_t *fg_pattern;
-
-/* For regex errors */
-#define RE_ERROR_BUF 512
-extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
-
-/* util.c */
-bool file_matching(const char *fname);
-int procfile(const char *fn);
-int grep_tree(char **argv);
-void *grep_malloc(size_t size);
-void *grep_calloc(size_t nmemb, size_t size);
-void *grep_realloc(void *ptr, size_t size);
-char *grep_strdup(const char *str);
-void printline(struct str *line, int sep, regmatch_t *matches, int m);
-
-/* queue.c */
-void enqueue(struct str *x);
-void printqueue(void);
-void clearqueue(void);
-
-/* file.c */
-void grep_close(struct file *f);
-struct file *grep_open(const char *path);
-char *grep_fgetln(struct file *f, size_t *len);
-
-/* fastgrep.c */
-int fastcomp(fastgrep_t *, const char *);
-void fgrepcomp(fastgrep_t *, const char *);
-int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
deleted file mode 100644
index e3c6be1..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/queue.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */
-/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * A really poor man's queue. It does only what it has to and gets out of
- * Dodge. It is used in place of <sys/queue.h> to get a better performance.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
-
-#include <sys/param.h>
-#include <sys/queue.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "grep.h"
-
-struct qentry {
- STAILQ_ENTRY(qentry) list;
- struct str data;
-};
-
-static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
-static unsigned long long count;
-
-static struct qentry *dequeue(void);
-
-void
-enqueue(struct str *x)
-{
- struct qentry *item;
-
- item = grep_malloc(sizeof(struct qentry));
- item->data.dat = grep_malloc(sizeof(char) * x->len);
- item->data.len = x->len;
- item->data.line_no = x->line_no;
- item->data.off = x->off;
- memcpy(item->data.dat, x->dat, x->len);
- item->data.file = x->file;
-
- STAILQ_INSERT_TAIL(&queue, item, list);
-
- if (++count > Bflag) {
- item = dequeue();
- free(item->data.dat);
- free(item);
- }
-}
-
-static struct qentry *
-dequeue(void)
-{
- struct qentry *item;
-
- item = STAILQ_FIRST(&queue);
- if (item == NULL)
- return (NULL);
-
- STAILQ_REMOVE_HEAD(&queue, list);
- --count;
- return (item);
-}
-
-void
-printqueue(void)
-{
- struct qentry *item;
-
- while ((item = dequeue()) != NULL) {
- printline(&item->data, '-', NULL, 0);
- free(item->data.dat);
- free(item);
- }
-}
-
-void
-clearqueue(void)
-{
- struct qentry *item;
-
- while ((item = dequeue()) != NULL) {
- free(item->data.dat);
- free(item);
- }
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
deleted file mode 100644
index ecd948d..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
-/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
-/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <fts.h>
-#include <libgen.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static bool first, first_global = true;
-static unsigned long long since_printed;
-
-static int procline(struct str *l, int);
-
-bool
-file_matching(const char *fname)
-{
- char *fname_base, *fname_copy;
- unsigned int i;
- bool ret;
-
- ret = finclude ? false : true;
- fname_copy = grep_strdup(fname);
- fname_base = basename(fname_copy);
-
- for (i = 0; i < fpatterns; ++i) {
- if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
- fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
- if (fpattern[i].mode == EXCL_PAT) {
- free(fname_copy);
- return (false);
- } else
- ret = true;
- }
- }
- free(fname_copy);
- return (ret);
-}
-
-static inline bool
-dir_matching(const char *dname)
-{
- unsigned int i;
- bool ret;
-
- ret = dinclude ? false : true;
-
- for (i = 0; i < dpatterns; ++i) {
- if (dname != NULL &&
- fnmatch(dname, dpattern[i].pat, 0) == 0) {
- if (dpattern[i].mode == EXCL_PAT)
- return (false);
- else
- ret = true;
- }
- }
- return (ret);
-}
-
-/*
- * Processes a directory when a recursive search is performed with
- * the -R option. Each appropriate file is passed to procfile().
- */
-int
-grep_tree(char **argv)
-{
- FTS *fts;
- FTSENT *p;
- char *d, *dir = NULL;
- int c, fts_flags;
- bool ok;
-
- c = fts_flags = 0;
-
- switch(linkbehave) {
- case LINK_EXPLICIT:
- fts_flags = FTS_COMFOLLOW;
- break;
- case LINK_SKIP:
- fts_flags = FTS_PHYSICAL;
- break;
- default:
- fts_flags = FTS_LOGICAL;
-
- }
-
- fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
-
- if (!(fts = fts_open(argv, fts_flags, NULL)))
- err(2, "fts_open");
- while ((p = fts_read(fts)) != NULL) {
- switch (p->fts_info) {
- case FTS_DNR:
- /* FALLTHROUGH */
- case FTS_ERR:
- errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
- break;
- case FTS_D:
- /* FALLTHROUGH */
- case FTS_DP:
- break;
- case FTS_DC:
- /* Print a warning for recursive directory loop */
- warnx("warning: %s: recursive directory loop",
- p->fts_path);
- break;
- default:
- /* Check for file exclusion/inclusion */
- ok = true;
- if (dexclude || dinclude) {
- if ((d = strrchr(p->fts_path, '/')) != NULL) {
- dir = grep_malloc(sizeof(char) *
- (d - p->fts_path + 1));
- memcpy(dir, p->fts_path,
- d - p->fts_path);
- dir[d - p->fts_path] = '\0';
- }
- ok = dir_matching(dir);
- free(dir);
- dir = NULL;
- }
- if (fexclude || finclude)
- ok &= file_matching(p->fts_path);
-
- if (ok)
- c += procfile(p->fts_path);
- break;
- }
- }
-
- fts_close(fts);
- return (c);
-}
-
-/*
- * Opens a file and processes it. Each file is processed line-by-line
- * passing the lines to procline().
- */
-int
-procfile(const char *fn)
-{
- struct file *f;
- struct stat sb;
- struct str ln;
- mode_t s;
- int c, t;
-
- if (mflag && (mcount <= 0))
- return (0);
-
- if (strcmp(fn, "-") == 0) {
- fn = label != NULL ? label : getstr(1);
- f = grep_open(NULL);
- } else {
- if (!stat(fn, &sb)) {
- /* Check if we need to process the file */
- s = sb.st_mode & S_IFMT;
- if (s == S_IFDIR && dirbehave == DIR_SKIP)
- return (0);
- if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
- || s == S_IFSOCK) && devbehave == DEV_SKIP)
- return (0);
- }
- f = grep_open(fn);
- }
- if (f == NULL) {
- if (!sflag)
- warn("%s", fn);
- if (errno == ENOENT)
- notfound = true;
- return (0);
- }
-
- ln.file = grep_malloc(strlen(fn) + 1);
- strcpy(ln.file, fn);
- ln.line_no = 0;
- ln.len = 0;
- tail = 0;
- ln.off = -1;
-
- for (first = true, c = 0; c == 0 || !(lflag || qflag); ) {
- ln.off += ln.len + 1;
- if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
- break;
- if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
- --ln.len;
- ln.line_no++;
-
- /* Return if we need to skip a binary file */
- if (f->binary && binbehave == BINFILE_SKIP) {
- grep_close(f);
- free(ln.file);
- free(f);
- return (0);
- }
- /* Process the file line-by-line */
- t = procline(&ln, f->binary);
- c += t;
-
- /* Count the matches if we have a match limit */
- if (mflag) {
- mcount -= t;
- if (mcount <= 0)
- break;
- }
- }
- if (Bflag > 0)
- clearqueue();
- grep_close(f);
-
- if (cflag) {
- if (!hflag)
- printf("%s:", ln.file);
- printf("%u%c", c, line_sep);
- }
- if (lflag && !qflag && c != 0)
- printf("%s%c", fn, line_sep);
- if (Lflag && !qflag && c == 0)
- printf("%s%c", fn, line_sep);
- if (c && !cflag && !lflag && !Lflag &&
- binbehave == BINFILE_BIN && f->binary && !qflag)
- printf(getstr(8), fn);
-
- free(ln.file);
- free(f);
- return (c);
-}
-
-#define iswword(x) (iswalnum((x)) || (x) == L'_')
-
-/*
- * Processes a line comparing it with the specified patterns. Each pattern
- * is looped to be compared along with the full string, saving each and every
- * match, which is necessary to colorize the output and to count the
- * matches. The matching lines are passed to printline() to display the
- * appropriate output.
- */
-static int
-procline(struct str *l, int nottext)
-{
- regmatch_t matches[MAX_LINE_MATCHES];
- regmatch_t pmatch;
- size_t st = 0;
- unsigned int i;
- int c = 0, m = 0, r = 0;
-
- /* Loop to process the whole line */
- while (st <= l->len) {
- pmatch.rm_so = st;
- pmatch.rm_eo = l->len;
-
- /* Loop to compare with all the patterns */
- for (i = 0; i < patterns; i++) {
-/*
- * XXX: grep_search() is a workaround for speed up and should be
- * removed in the future. See fastgrep.c.
- */
- if (fg_pattern[i].pattern) {
- r = grep_search(&fg_pattern[i],
- (unsigned char *)l->dat,
- l->len, &pmatch);
- r = (r == 0) ? 0 : REG_NOMATCH;
- st = pmatch.rm_eo;
- } else {
- r = regexec(&r_pattern[i], l->dat, 1,
- &pmatch, eflags);
- r = (r == 0) ? 0 : REG_NOMATCH;
- st = pmatch.rm_eo;
- }
- if (r == REG_NOMATCH)
- continue;
- /* Check for full match */
- if (xflag &&
- (pmatch.rm_so != 0 ||
- (size_t)pmatch.rm_eo != l->len))
- continue;
- /* Check for whole word match */
- if (fg_pattern[i].word && pmatch.rm_so != 0) {
- wchar_t wbegin, wend;
-
- wbegin = wend = L' ';
- if (pmatch.rm_so != 0 &&
- sscanf(&l->dat[pmatch.rm_so - 1],
- "%lc", &wbegin) != 1)
- continue;
- if ((size_t)pmatch.rm_eo != l->len &&
- sscanf(&l->dat[pmatch.rm_eo],
- "%lc", &wend) != 1)
- continue;
- if (iswword(wbegin) || iswword(wend))
- continue;
- }
- c = 1;
- if (m < MAX_LINE_MATCHES)
- matches[m++] = pmatch;
- /* matches - skip further patterns */
- if ((color != NULL && !oflag) || qflag || lflag)
- break;
- }
-
- if (vflag) {
- c = !c;
- break;
- }
- /* One pass if we are not recording matches */
- if ((color != NULL && !oflag) || qflag || lflag)
- break;
-
- if (st == (size_t)pmatch.rm_so)
- break; /* No matches */
- }
-
- if (c && binbehave == BINFILE_BIN && nottext)
- return (c); /* Binary file */
-
- /* Dealing with the context */
- if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
- if (c) {
- if ((Aflag || Bflag) && !first_global &&
- (first || since_printed > Bflag))
- printf("--\n");
- tail = Aflag;
- if (Bflag > 0)
- printqueue();
- printline(l, ':', matches, m);
- } else {
- printline(l, '-', matches, m);
- tail--;
- }
- first = false;
- first_global = false;
- since_printed = 0;
- } else {
- if (Bflag)
- enqueue(l);
- since_printed++;
- }
- return (c);
-}
-
-/*
- * Safe malloc() for internal use.
- */
-void *
-grep_malloc(size_t size)
-{
- void *ptr;
-
- if ((ptr = malloc(size)) == NULL)
- err(2, "malloc");
- return (ptr);
-}
-
-/*
- * Safe calloc() for internal use.
- */
-void *
-grep_calloc(size_t nmemb, size_t size)
-{
- void *ptr;
-
- if ((ptr = calloc(nmemb, size)) == NULL)
- err(2, "calloc");
- return (ptr);
-}
-
-/*
- * Safe realloc() for internal use.
- */
-void *
-grep_realloc(void *ptr, size_t size)
-{
-
- if ((ptr = realloc(ptr, size)) == NULL)
- err(2, "realloc");
- return (ptr);
-}
-
-/*
- * Safe strdup() for internal use.
- */
-char *
-grep_strdup(const char *str)
-{
- char *ret;
-
- if ((ret = strdup(str)) == NULL)
- err(2, "strdup");
- return (ret);
-}
-
-/*
- * Prints a matching line according to the command line options.
- */
-void
-printline(struct str *line, int sep, regmatch_t *matches, int m)
-{
- size_t a = 0;
- int i, n = 0;
-
- if (!hflag) {
- if (nullflag == 0)
- fputs(line->file, stdout);
- else {
- printf("%s", line->file);
- putchar(0);
- }
- ++n;
- }
- if (nflag) {
- if (n > 0)
- putchar(sep);
- printf("%d", line->line_no);
- ++n;
- }
- if (bflag) {
- if (n > 0)
- putchar(sep);
- printf("%lld", (long long)line->off);
- ++n;
- }
- if (n)
- putchar(sep);
- /* --color and -o */
- if ((oflag || color) && m > 0) {
- for (i = 0; i < m; i++) {
- if (!oflag)
- fwrite(line->dat + a, matches[i].rm_so - a, 1,
- stdout);
- if (color)
- fprintf(stdout, "\33[%sm\33[K", color);
-
- fwrite(line->dat + matches[i].rm_so,
- matches[i].rm_eo - matches[i].rm_so, 1,
- stdout);
- if (color)
- fprintf(stdout, "\33[m\33[K");
- a = matches[i].rm_eo;
- if (oflag)
- putchar('\n');
- }
- if (!oflag) {
- if (line->len - a > 0)
- fwrite(line->dat + a, line->len - a, 1, stdout);
- putchar(line_sep);
- }
- } else {
- fwrite(line->dat, line->len, 1, stdout);
- putchar(line_sep);
- }
-}
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 65b271a..1666cfb 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -1,4 +1,3 @@
-//
// Copyright (C) 2015 The Android Open-Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,14 +19,15 @@
// to only building on ARM if they include assembly. Individual makefiles
// are responsible for having their own logic, for fine-grained control.
-cc_library_shared {
- name: "gatekeeper.trusty",
+cc_binary {
+ name: "android.hardware.gatekeeper@1.0-service.trusty",
+ defaults: ["hidl_defaults"],
vendor: true,
-
relative_install_path: "hw",
+ init_rc: ["android.hardware.gatekeeper@1.0-service.trusty.rc"],
srcs: [
- "module.cpp",
+ "service.cpp",
"trusty_gatekeeper_ipc.c",
"trusty_gatekeeper.cpp",
],
@@ -39,10 +39,16 @@
],
shared_libs: [
+ "android.hardware.gatekeeper@1.0",
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
"libgatekeeper",
+ "libutils",
"liblog",
"libcutils",
"libtrusty",
],
- header_libs: ["libhardware_headers"],
+
+ vintf_fragments: ["android.hardware.gatekeeper@1.0-service.trusty.xml"],
}
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
new file mode 100644
index 0000000..5413a6c
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper-1-0 /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.trusty
+ class hal
+ user system
+ group system
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
new file mode 100644
index 0000000..19714a8
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.gatekeeper</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IGatekeeper</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
deleted file mode 100644
index 0ee3c2f..0000000
--- a/trusty/gatekeeper/module.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hardware/hardware.h>
-
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "trusty_gatekeeper.h"
-
-using gatekeeper::TrustyGateKeeperDevice;
-
-static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
- hw_device_t **device) {
-
- if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
- return -EINVAL;
- }
-
- TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
- if (gatekeeper == NULL) return -ENOMEM;
- *device = gatekeeper->hw_device();
-
- return 0;
-}
-
-static struct hw_module_methods_t gatekeeper_module_methods = {
- .open = trusty_gatekeeper_open,
-};
-
-struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
- .common = {
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = GATEKEEPER_HARDWARE_MODULE_ID,
- .name = "Trusty GateKeeper HAL",
- .author = "The Android Open Source Project",
- .methods = &gatekeeper_module_methods,
- .dso = 0,
- .reserved = {}
- },
-};
diff --git a/trusty/gatekeeper/service.cpp b/trusty/gatekeeper/service.cpp
new file mode 100644
index 0000000..c5ee488
--- /dev/null
+++ b/trusty/gatekeeper/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.gatekeeper@1.0-service.trusty"
+
+#include <android-base/logging.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <hidl/LegacySupport.h>
+
+#include "trusty_gatekeeper.h"
+
+// Generated HIDL files
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using gatekeeper::TrustyGateKeeperDevice;
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
+ android::sp<TrustyGateKeeperDevice> gatekeeper(new TrustyGateKeeperDevice());
+ auto status = gatekeeper->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Gatekeeper 1.0 (trusty) (" << status << ")";
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index b3fbfa9..d149664 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -16,147 +16,131 @@
#define LOG_TAG "TrustyGateKeeper"
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <type_traits>
-
-#include <log/log.h>
+#include <android-base/logging.h>
+#include <limits>
#include "trusty_gatekeeper.h"
#include "trusty_gatekeeper_ipc.h"
#include "gatekeeper_ipc.h"
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::ERROR_INVALID;
+using ::gatekeeper::ERROR_MEMORY_ALLOCATION_FAILED;
+using ::gatekeeper::ERROR_NONE;
+using ::gatekeeper::ERROR_RETRY;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
namespace gatekeeper {
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+constexpr const uint32_t SEND_BUF_SIZE = 8192;
+constexpr const uint32_t RECV_BUF_SIZE = 8192;
-TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
- static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
- "TrustyGateKeeperDevice must be standard layout");
- static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
- "device_ must be the first member of TrustyGateKeeperDevice");
- static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
- "common must be the first member of gatekeeper_device");
-#else
- assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
- assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
-#endif
-
- memset(&device_, 0, sizeof(device_));
- device_.common.tag = HARDWARE_DEVICE_TAG;
- device_.common.version = 1;
- device_.common.module = const_cast<hw_module_t *>(module);
- device_.common.close = close_device;
-
- device_.enroll = enroll;
- device_.verify = verify;
- device_.delete_user = nullptr;
- device_.delete_all_users = nullptr;
-
+TrustyGateKeeperDevice::TrustyGateKeeperDevice() {
int rc = trusty_gatekeeper_connect();
if (rc < 0) {
- ALOGE("Error initializing trusty session: %d", rc);
+ LOG(ERROR) << "Error initializing trusty session: " << rc;
}
error_ = rc;
-
-}
-
-hw_device_t* TrustyGateKeeperDevice::hw_device() {
- return &device_.common;
-}
-
-int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
- delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
- return 0;
}
TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
trusty_gatekeeper_disconnect();
}
-int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
- uint32_t current_password_handle_length, const uint8_t *current_password,
- uint32_t current_password_length, const uint8_t *desired_password,
- uint32_t desired_password_length, uint8_t **enrolled_password_handle,
- uint32_t *enrolled_password_handle_length) {
-
- if (error_ != 0) {
- return error_;
- }
-
- SizedBuffer desired_password_buffer(desired_password_length);
- memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
- SizedBuffer current_password_handle_buffer(current_password_handle_length);
- if (current_password_handle) {
- memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
- current_password_handle_length);
- }
-
- SizedBuffer current_password_buffer(current_password_length);
- if (current_password) {
- memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
- }
-
- EnrollRequest request(uid, ¤t_password_handle_buffer, &desired_password_buffer,
- ¤t_password_buffer);
- EnrollResponse response;
-
- gatekeeper_error_t error = Send(request, &response);
-
- if (error == ERROR_RETRY) {
- return response.retry_timeout;
- } else if (error != ERROR_NONE) {
- return -EINVAL;
- }
-
- *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
- *enrolled_password_handle_length = response.enrolled_password_handle.length;
-
-
- return 0;
+SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
+ if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
+ auto dummy = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), dummy);
+ return {dummy, static_cast<uint32_t>(vec.size())};
}
-int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
+ const hidl_vec<uint8_t>& currentPasswordHandle,
+ const hidl_vec<uint8_t>& currentPassword,
+ const hidl_vec<uint8_t>& desiredPassword,
+ enroll_cb _hidl_cb) {
if (error_ != 0) {
- return error_;
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
}
- SizedBuffer password_handle_buffer(enrolled_password_handle_length);
- memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
- enrolled_password_handle_length);
- SizedBuffer provided_password_buffer(provided_password_length);
- memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+ if (desiredPassword.size() == 0) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
+ }
- VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+ EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
+ hidl_vec2sized_buffer(desiredPassword),
+ hidl_vec2sized_buffer(currentPassword));
+ EnrollResponse response;
+ auto error = Send(request, &response);
+ if (error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else if (response.error == ERROR_RETRY) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+ } else if (response.error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else {
+ hidl_vec<uint8_t> new_handle(response.enrolled_password_handle.Data<uint8_t>(),
+ response.enrolled_password_handle.Data<uint8_t>() +
+ response.enrolled_password_handle.size());
+ _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, new_handle});
+ }
+ return {};
+}
+
+Return<void> TrustyGateKeeperDevice::verify(
+ uint32_t uid, uint64_t challenge,
+ const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& providedPassword, verify_cb _hidl_cb) {
+ if (error_ != 0) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
+ }
+
+ if (enrolledPasswordHandle.size() == 0) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ return {};
+ }
+
+ VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
+ hidl_vec2sized_buffer(providedPassword));
VerifyResponse response;
- gatekeeper_error_t error = Send(request, &response);
+ auto error = Send(request, &response);
+ if (error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else if (response.error == ERROR_RETRY) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+ } else if (response.error != ERROR_NONE) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+ } else {
+ hidl_vec<uint8_t> auth_token(
+ response.auth_token.Data<uint8_t>(),
+ response.auth_token.Data<uint8_t>() + response.auth_token.size());
- if (error == ERROR_RETRY) {
- return response.retry_timeout;
- } else if (error != ERROR_NONE) {
- return -EINVAL;
+ _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
+ : GatekeeperStatusCode::STATUS_OK,
+ response.retry_timeout, auth_token});
}
+ return {};
+}
- if (auth_token != NULL && auth_token_length != NULL) {
- *auth_token = response.auth_token.buffer.release();
- *auth_token_length = response.auth_token.length;
- }
+Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+ return {};
+}
- if (request_reenroll != NULL) {
- *request_reenroll = response.request_reenroll;
- }
-
- return 0;
+Return<void> TrustyGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
+ _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+ return {};
}
gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
@@ -172,7 +156,7 @@
uint32_t response_size = RECV_BUF_SIZE;
int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
if (rc < 0) {
- ALOGE("error (%d) calling gatekeeper TA", rc);
+ LOG(ERROR) << "error (" << rc << ") calling gatekeeper TA";
return ERROR_INVALID;
}
@@ -182,51 +166,4 @@
return response->Deserialize(payload, payload + response_size);
}
-static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
- return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
-}
-
-/* static */
-int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
- if (dev == NULL ||
- enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
- desired_password == NULL || desired_password_length == 0)
- return -EINVAL;
-
- // Current password and current password handle go together
- if (current_password_handle == NULL || current_password_handle_length == 0 ||
- current_password == NULL || current_password_length == 0) {
- current_password_handle = NULL;
- current_password_handle_length = 0;
- current_password = NULL;
- current_password_length = 0;
- }
-
- return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
- current_password, current_password_length, desired_password, desired_password_length,
- enrolled_password_handle, enrolled_password_handle_length);
-
-}
-
-/* static */
-int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
- uint64_t challenge, const uint8_t *enrolled_password_handle,
- uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
- uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
- bool *request_reenroll) {
-
- if (dev == NULL || enrolled_password_handle == NULL ||
- provided_password == NULL) {
- return -EINVAL;
- }
-
- return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
- enrolled_password_handle_length, provided_password, provided_password_length,
- auth_token, auth_token_length, request_reenroll);
-}
};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
index 2becc49..c0713f4 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -17,84 +17,34 @@
#ifndef TRUSTY_GATEKEEPER_H
#define TRUSTY_GATEKEEPER_H
-#include <hardware/gatekeeper.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <hidl/Status.h>
+
+#include <memory>
+
#include <gatekeeper/gatekeeper_messages.h>
#include "gatekeeper_ipc.h"
namespace gatekeeper {
-class TrustyGateKeeperDevice {
- public:
-
- explicit TrustyGateKeeperDevice(const hw_module_t* module);
+class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
+ public:
+ explicit TrustyGateKeeperDevice();
~TrustyGateKeeperDevice();
-
- hw_device_t* hw_device();
-
/**
* Enrolls password_payload, which should be derived from a user selected pin or password,
* with the authentication factor private key used only for enrolling authentication
* factor data.
*
* Returns: 0 on success or an error code less than 0 on error.
- * On error, enrolled_password will not be allocated.
- */
- int Enroll(uint32_t uid, const uint8_t *current_password_handle,
- uint32_t current_password_handle_length, const uint8_t *current_password,
- uint32_t current_password_length, const uint8_t *desired_password,
- uint32_t desired_password_length, uint8_t **enrolled_password_handle,
- uint32_t *enrolled_password_handle_length);
-
- /**
- * Verifies provided_password matches expected_password after enrolling
- * with the authentication factor private key.
- *
- * Implementations of this module may retain the result of this call
- * to attest to the recency of authentication.
- *
- * On success, writes the address of a verification token to verification_token,
- *
- * Returns: 0 on success or an error code less than 0 on error
- * On error, verification token will not be allocated
- */
- int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
- uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
- uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
- bool *request_reenroll);
-
- private:
-
- gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
- GateKeeperMessage* response);
-
- gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
- return Send(GK_ENROLL, request, response);
- }
-
- gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
- return Send(GK_VERIFY, request, response);
- }
-
- // Static methods interfacing the HAL API with the TrustyGateKeeper device
-
- /**
- * Enrolls desired_password, which should be derived from a user selected pin or password,
- * with the authentication factor private key used only for enrolling authentication
- * factor data.
- *
- * If there was already a password enrolled, it should be provided in
- * current_password_handle, along with the current password in current_password
- * that should validate against current_password_handle.
- *
- * Returns: 0 on success or an error code less than 0 on error.
* On error, enrolled_password_handle will not be allocated.
*/
- static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
- const uint8_t *current_password_handle, uint32_t current_password_handle_length,
- const uint8_t *current_password, uint32_t current_password_length,
- const uint8_t *desired_password, uint32_t desired_password_length,
- uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+ ::android::hardware::Return<void> enroll(
+ uint32_t uid, const ::android::hardware::hidl_vec<uint8_t>& currentPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& currentPassword,
+ const ::android::hardware::hidl_vec<uint8_t>& desiredPassword,
+ enroll_cb _hidl_cb) override;
/**
* Verifies provided_password matches enrolled_password_handle.
@@ -109,18 +59,32 @@
* Returns: 0 on success or an error code less than 0 on error
* On error, verification token will not be allocated
*/
- static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
- const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
- const uint8_t *provided_password, uint32_t provided_password_length,
- uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+ ::android::hardware::Return<void> verify(
+ uint32_t uid, uint64_t challenge,
+ const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+ const ::android::hardware::hidl_vec<uint8_t>& providedPassword,
+ verify_cb _hidl_cb) override;
- static int close_device(hw_device_t* dev);
+ ::android::hardware::Return<void> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
- gatekeeper_device device_;
+ ::android::hardware::Return<void> deleteAllUsers(deleteAllUsers_cb _hidl_cb) override;
+
+ private:
+ gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+ GateKeeperMessage* response);
+
+ gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+ return Send(GK_ENROLL, request, response);
+ }
+
+ gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+ return Send(GK_VERIFY, request, response);
+ }
+
int error_;
-
};
-}
+
+} // namespace gatekeeper
#endif
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
new file mode 100644
index 0000000..b5fc6bf
--- /dev/null
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -0,0 +1,571 @@
+/*
+ **
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
+
+#include <authorization_set.h>
+#include <cutils/log.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::AddEntropyRequest;
+using ::keymaster::AddEntropyResponse;
+using ::keymaster::AttestKeyRequest;
+using ::keymaster::AttestKeyResponse;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::BeginOperationRequest;
+using ::keymaster::BeginOperationResponse;
+using ::keymaster::ExportKeyRequest;
+using ::keymaster::ExportKeyResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::GenerateKeyRequest;
+using ::keymaster::GenerateKeyResponse;
+using ::keymaster::GetKeyCharacteristicsRequest;
+using ::keymaster::GetKeyCharacteristicsResponse;
+using ::keymaster::ImportKeyRequest;
+using ::keymaster::ImportKeyResponse;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using ::keymaster::ng::Tag;
+
+namespace keymaster {
+namespace V4_0 {
+namespace {
+
+inline keymaster_tag_t legacy_enum_conversion(const Tag value) {
+ return keymaster_tag_t(value);
+}
+inline Tag legacy_enum_conversion(const keymaster_tag_t value) {
+ return Tag(value);
+}
+inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {
+ return keymaster_purpose_t(value);
+}
+inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {
+ return keymaster_key_format_t(value);
+}
+
+inline SecurityLevel legacy_enum_conversion(const keymaster_security_level_t value) {
+ return static_cast<SecurityLevel>(value);
+}
+
+inline hw_authenticator_type_t legacy_enum_conversion(const HardwareAuthenticatorType value) {
+ return static_cast<hw_authenticator_type_t>(value);
+}
+
+inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {
+ return ErrorCode(value);
+}
+
+inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {
+ return keymaster_tag_get_type(tag);
+}
+
+class KmParamSet : public keymaster_key_param_set_t {
+ public:
+ KmParamSet(const hidl_vec<KeyParameter>& keyParams) {
+ params = new keymaster_key_param_t[keyParams.size()];
+ length = keyParams.size();
+ for (size_t i = 0; i < keyParams.size(); ++i) {
+ auto tag = legacy_enum_conversion(keyParams[i].tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ params[i] = keymaster_param_int(tag, keyParams[i].f.integer);
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);
+ break;
+ case KM_DATE:
+ params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);
+ break;
+ case KM_BOOL:
+ if (keyParams[i].f.boolValue)
+ params[i] = keymaster_param_bool(tag);
+ else
+ params[i].tag = KM_TAG_INVALID;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],
+ keyParams[i].blob.size());
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ }
+ KmParamSet(KmParamSet&& other) noexcept
+ : keymaster_key_param_set_t{other.params, other.length} {
+ other.length = 0;
+ other.params = nullptr;
+ }
+ KmParamSet(const KmParamSet&) = delete;
+ ~KmParamSet() { delete[] params; }
+};
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);
+ return result;
+}
+
+inline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());
+ return result;
+}
+
+inline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(
+ const keymaster_cert_chain_t& cert_chain) {
+ hidl_vec<hidl_vec<uint8_t>> result;
+ if (!cert_chain.entry_count || !cert_chain.entries) return result;
+
+ result.resize(cert_chain.entry_count);
+ for (size_t i = 0; i < cert_chain.entry_count; ++i) {
+ result[i] = kmBlob2hidlVec(cert_chain.entries[i]);
+ }
+
+ return result;
+}
+
+static inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {
+ hidl_vec<KeyParameter> result;
+ if (set.length == 0 || set.params == nullptr) return result;
+
+ result.resize(set.length);
+ keymaster_key_param_t* params = set.params;
+ for (size_t i = 0; i < set.length; ++i) {
+ auto tag = params[i].tag;
+ result[i].tag = legacy_enum_conversion(tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ result[i].f.integer = params[i].enumerated;
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ result[i].f.integer = params[i].integer;
+ break;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ result[i].f.longInteger = params[i].long_integer;
+ break;
+ case KM_DATE:
+ result[i].f.dateTime = params[i].date_time;
+ break;
+ case KM_BOOL:
+ result[i].f.boolValue = params[i].boolean;
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),
+ params[i].blob.data_length);
+ break;
+ case KM_INVALID:
+ default:
+ params[i].tag = KM_TAG_INVALID;
+ /* just skip */
+ break;
+ }
+ }
+ return result;
+}
+
+void addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ ::keymaster::AuthorizationSet* params) {
+ params->Clear();
+ if (clientId.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());
+ }
+ if (appData.size()) {
+ params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());
+ }
+}
+
+} // anonymous namespace
+
+TrustyKeymaster4Device::TrustyKeymaster4Device(TrustyKeymaster* impl) : impl_(impl) {}
+
+TrustyKeymaster4Device::~TrustyKeymaster4Device() {}
+
+Return<void> TrustyKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) {
+ _hidl_cb(SecurityLevel::TRUSTED_ENVIRONMENT, "TrustyKeymaster", "Google");
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getHmacSharingParameters(
+ getHmacSharingParameters_cb _hidl_cb) {
+ const GetHmacSharingParametersResponse response = impl_->GetHmacSharingParameters();
+ // response.params is not the same as the HIDL structure, we need to convert it
+ V4_0::HmacSharingParameters params;
+ params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data),
+ response.params.seed.data_length);
+ static_assert(sizeof(response.params.nonce) == params.nonce.size(), "Nonce sizes don't match");
+ memcpy(params.nonce.data(), response.params.nonce, params.nonce.size());
+ _hidl_cb(legacy_enum_conversion(response.error), params);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::computeSharedHmac(
+ const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
+ ComputeSharedHmacRequest request;
+ request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
+ request.params_array.num_params = params.size();
+ for (size_t i = 0; i < params.size(); ++i) {
+ request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
+ static_assert(sizeof(request.params_array.params_array[i].nonce) ==
+ decltype(params[i].nonce)::size(),
+ "Nonce sizes don't match");
+ memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+ params[i].nonce.size());
+ }
+
+ auto response = impl_->ComputeSharedHmac(request);
+ hidl_vec<uint8_t> sharing_check;
+ if (response.error == KM_ERROR_OK) {
+ sharing_check = kmBlob2hidlVec(response.sharing_check);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), sharing_check);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::verifyAuthorization(
+ uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
+ VerifyAuthorizationRequest request;
+ request.challenge = challenge;
+ request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
+ request.auth_token.challenge = authToken.challenge;
+ request.auth_token.user_id = authToken.userId;
+ request.auth_token.authenticator_id = authToken.authenticatorId;
+ request.auth_token.authenticator_type = legacy_enum_conversion(authToken.authenticatorType);
+ request.auth_token.timestamp = authToken.timestamp;
+ KeymasterBlob mac(authToken.mac.data(), authToken.mac.size());
+ request.auth_token.mac = mac;
+
+ auto response = impl_->VerifyAuthorization(request);
+
+ ::android::hardware::keymaster::V4_0::VerificationToken token;
+ token.challenge = response.token.challenge;
+ token.timestamp = response.token.timestamp;
+ token.parametersVerified = kmParamSet2Hidl(response.token.parameters_verified);
+ token.securityLevel = legacy_enum_conversion(response.token.security_level);
+ token.mac = kmBlob2hidlVec(response.token.mac);
+
+ _hidl_cb(legacy_enum_conversion(response.error), token);
+
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
+ if (data.size() == 0) return ErrorCode::OK;
+ AddEntropyRequest request;
+ request.random_data.Reinitialize(data.data(), data.size());
+
+ AddEntropyResponse response;
+ impl_->AddRngEntropy(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) {
+ GenerateKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(keyParams));
+
+ GenerateKeyResponse response;
+ impl_->GenerateKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) {
+ GetKeyCharacteristicsRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ GetKeyCharacteristicsResponse response;
+ impl_->GetKeyCharacteristics(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ if (response.error == KM_ERROR_OK) {
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importKey(const hidl_vec<KeyParameter>& params,
+ KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData,
+ importKey_cb _hidl_cb) {
+ ImportKeyRequest request;
+ request.key_description.Reinitialize(KmParamSet(params));
+ request.key_format = legacy_enum_conversion(keyFormat);
+ request.SetKeyMaterial(keyData.data(), keyData.size());
+
+ ImportKeyResponse response;
+ impl_->ImportKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::importWrappedKey(
+ const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
+ ImportWrappedKeyRequest request;
+ request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+ request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+ request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+ request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+ request.password_sid = passwordSid;
+ request.biometric_sid = biometricSid;
+
+ ImportWrappedKeyResponse response;
+ impl_->ImportWrappedKey(request, &response);
+
+ KeyCharacteristics resultCharacteristics;
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob = kmBlob2hidlVec(response.key_blob);
+ resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);
+ resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,
+ const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) {
+ ExportKeyRequest request;
+ request.key_format = legacy_enum_conversion(exportFormat);
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+ addClientAndAppData(clientId, appData, &request.additional_params);
+
+ ExportKeyResponse response;
+ impl_->ExportKey(request, &response);
+
+ hidl_vec<uint8_t> resultKeyBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultKeyBlob.setToExternal(response.key_data, response.key_data_length);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) {
+ AttestKeyRequest request;
+ request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
+ request.attest_params.Reinitialize(KmParamSet(attestParams));
+
+ AttestKeyResponse response;
+ impl_->AttestKey(request, &response);
+
+ hidl_vec<hidl_vec<uint8_t>> resultCertChain;
+ if (response.error == KM_ERROR_OK) {
+ resultCertChain = kmCertChain2Hidl(response.certificate_chain);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) {
+ UpgradeKeyRequest request;
+ request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+ request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+ UpgradeKeyResponse response;
+ impl_->UpgradeKey(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));
+ } else {
+ _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());
+ }
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
+ DeleteKeyRequest request;
+ request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+ DeleteKeyResponse response;
+ impl_->DeleteKey(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
+ DeleteAllKeysRequest request;
+ DeleteAllKeysResponse response;
+ impl_->DeleteAllKeys(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::destroyAttestationIds() {
+ return ErrorCode::UNIMPLEMENTED;
+}
+
+Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams,
+ const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
+ (void)authToken;
+ BeginOperationRequest request;
+ request.purpose = legacy_enum_conversion(purpose);
+ request.SetKeyMaterial(key.data(), key.size());
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ BeginOperationResponse response;
+ impl_->BeginOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ }
+
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::update(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ update_cb _hidl_cb) {
+ (void)authToken;
+ (void)verificationToken;
+ UpdateOperationRequest request;
+ UpdateOperationResponse response;
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ uint32_t resultConsumed = 0;
+
+ request.op_handle = operationHandle;
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ size_t inp_size = input.size();
+ size_t ser_size = request.SerializedSize();
+
+ if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ response.error = KM_ERROR_INVALID_INPUT_LENGTH;
+ } else {
+ if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+ inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;
+ }
+ request.input.Reinitialize(input.data(), inp_size);
+
+ impl_->UpdateOperation(request, &response);
+
+ if (response.error == KM_ERROR_OK) {
+ resultConsumed = response.input_consumed;
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);
+ return Void();
+}
+
+Return<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,
+ const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input,
+ const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken,
+ finish_cb _hidl_cb) {
+ (void)authToken;
+ (void)verificationToken;
+ FinishOperationRequest request;
+ request.op_handle = operationHandle;
+ request.input.Reinitialize(input.data(), input.size());
+ request.signature.Reinitialize(signature.data(), signature.size());
+ request.additional_params.Reinitialize(KmParamSet(inParams));
+
+ FinishOperationResponse response;
+ impl_->FinishOperation(request, &response);
+
+ hidl_vec<KeyParameter> resultParams;
+ hidl_vec<uint8_t> resultBlob;
+ if (response.error == KM_ERROR_OK) {
+ resultParams = kmParamSet2Hidl(response.output_params);
+ resultBlob = kmBuffer2hidlVec(response.output);
+ }
+ _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);
+ return Void();
+}
+
+Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
+ AbortOperationRequest request;
+ request.op_handle = operationHandle;
+
+ AbortOperationResponse response;
+ impl_->AbortOperation(request, &response);
+
+ return legacy_enum_conversion(response.error);
+}
+} // namespace V4_0
+} // namespace keymaster
diff --git a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
new file mode 100644
index 0000000..72c9167
--- /dev/null
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymaster-4-0 /vendor/bin/hw/android.hardware.keymaster@4.0-service.trusty
+ class early_hal
+ user nobody
+ group drmrpc
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
new file mode 100644
index 0000000..96eb584
--- /dev/null
+++ b/trusty/keymaster/4.0/service.cpp
@@ -0,0 +1,43 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <android-base/logging.h>
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/HidlTransportSupport.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+#include <trusty_keymaster/TrustyKeymaster4Device.h>
+
+int main() {
+ ::android::hardware::configureRpcThreadpool(1, true);
+ auto trustyKeymaster = new keymaster::TrustyKeymaster();
+ int err = trustyKeymaster->Initialize();
+ if (err != 0) {
+ LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
+ return -1;
+ }
+
+ auto keymaster = new ::keymaster::V4_0::TrustyKeymaster4Device(trustyKeymaster);
+
+ auto status = keymaster->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for Keymaster 4.0 (" << status << ")";
+ return -1;
+ }
+
+ android::hardware::joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 819851f..d107b78 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -108,3 +108,34 @@
"android.hardware.keymaster@3.0"
],
}
+
+cc_binary {
+ name: "android.hardware.keymaster@4.0-service.trusty",
+ defaults: ["hidl_defaults"],
+ relative_install_path: "hw",
+ vendor: true,
+ init_rc: ["4.0/android.hardware.keymaster@4.0-service.trusty.rc"],
+ srcs: [
+ "4.0/service.cpp",
+ "4.0/TrustyKeymaster4Device.cpp",
+ "ipc/trusty_keymaster_ipc.cpp",
+ "TrustyKeymaster.cpp",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libdl",
+ "libbase",
+ "libutils",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "libtrusty",
+ "libkeymaster_messages",
+ "libkeymaster4",
+ "android.hardware.keymaster@4.0"
+ ],
+}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 7f5e87f..f3ef747 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -172,24 +172,25 @@
ForwardCommand(KM_ABORT_OPERATION, request, response);
}
-/* Methods for Keymaster 4.0 functionality -- not yet implemented */
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
+ // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ Buffer request;
GetHmacSharingParametersResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
return response;
}
ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
- const ComputeSharedHmacRequest& /* request */) {
+ const ComputeSharedHmacRequest& request) {
ComputeSharedHmacResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
return response;
}
VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
- const VerifyAuthorizationRequest& /* request */) {
+ const VerifyAuthorizationRequest& request) {
VerifyAuthorizationResponse response;
- response.error = KM_ERROR_UNIMPLEMENTED;
+ ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
return response;
}
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
new file mode 100644
index 0000000..2be15bc
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
@@ -0,0 +1,105 @@
+/*
+ **
+ ** Copyright 2017, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef keymaster_V4_0_TrustyKeymaster4Device_H_
+#define keymaster_V4_0_TrustyKeymaster4Device_H_
+
+#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>
+#include <hidl/Status.h>
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace keymaster {
+
+namespace V4_0 {
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::keymaster::V4_0::ErrorCode;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HmacSharingParameters;
+using ::android::hardware::keymaster::V4_0::IKeymasterDevice;
+using ::android::hardware::keymaster::V4_0::KeyCharacteristics;
+using ::android::hardware::keymaster::V4_0::KeyFormat;
+using ::android::hardware::keymaster::V4_0::KeyParameter;
+using ::android::hardware::keymaster::V4_0::KeyPurpose;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+using ::android::hardware::keymaster::V4_0::Tag;
+using ::android::hardware::keymaster::V4_0::VerificationToken;
+
+class TrustyKeymaster4Device : public IKeymasterDevice {
+ public:
+ explicit TrustyKeymaster4Device(TrustyKeymaster* impl);
+ virtual ~TrustyKeymaster4Device();
+
+ Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override;
+ Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override;
+ Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,
+ computeSharedHmac_cb) override;
+ Return<void> verifyAuthorization(uint64_t challenge,
+ const hidl_vec<KeyParameter>& parametersToVerify,
+ const HardwareAuthToken& authToken,
+ verifyAuthorization_cb _hidl_cb) override;
+ Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
+ Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
+ generateKey_cb _hidl_cb) override;
+ Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId,
+ const hidl_vec<uint8_t>& appData,
+ getKeyCharacteristics_cb _hidl_cb) override;
+ Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,
+ const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;
+ Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,
+ const hidl_vec<uint8_t>& wrappingKeyBlob,
+ const hidl_vec<uint8_t>& maskingKey,
+ const hidl_vec<KeyParameter>& unwrappingParams,
+ uint64_t passwordSid, uint64_t biometricSid,
+ importWrappedKey_cb _hidl_cb) override;
+ Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,
+ const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,
+ exportKey_cb _hidl_cb) override;
+ Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,
+ const hidl_vec<KeyParameter>& attestParams,
+ attestKey_cb _hidl_cb) override;
+ Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
+ const hidl_vec<KeyParameter>& upgradeParams,
+ upgradeKey_cb _hidl_cb) override;
+ Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;
+ Return<ErrorCode> deleteAllKeys() override;
+ Return<ErrorCode> destroyAttestationIds() override;
+ Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
+ const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,
+ begin_cb _hidl_cb) override;
+ Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, update_cb _hidl_cb) override;
+ Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,
+ const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const VerificationToken& verificationToken, finish_cb _hidl_cb) override;
+ Return<ErrorCode> abort(uint64_t operationHandle) override;
+
+ private:
+ std::unique_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+} // namespace V4_0
+} // namespace keymaster
+
+#endif // keymaster_V4_0_TrustyKeymaster4Device_H_
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 32499e3..9676b79 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -17,12 +17,10 @@
vendor: true,
srcs: ["tipc_test.c"],
- static_libs: [
- "libtrusty",
- ],
shared_libs: [
"libc",
"liblog",
+ "libtrusty",
],
gtest: false,
cflags: [
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 41263e5..c61f7d0 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -17,8 +17,8 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/stat.h>
@@ -34,28 +34,38 @@
#define REQ_BUFFER_SIZE 4096
static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
-static const char *ss_data_root;
-static const char *trusty_devname;
-static const char *rpmb_devname;
-static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* ss_data_root;
+static const char* trusty_devname;
+static const char* rpmb_devname;
+static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
-static const char *_sopts = "hp:d:r:";
-static const struct option _lopts[] = {
- {"help", no_argument, NULL, 'h'},
- {"trusty_dev", required_argument, NULL, 'd'},
- {"data_path", required_argument, NULL, 'p'},
- {"rpmb_dev", required_argument, NULL, 'r'},
- {0, 0, 0, 0}
-};
+static enum dev_type dev_type = MMC_RPMB;
-static void show_usage_and_exit(int code)
-{
- ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
+static enum dev_type parse_dev_type(const char* dev_type_name) {
+ if (!strcmp(dev_type_name, "mmc")) {
+ return MMC_RPMB;
+ } else if (!strcmp(dev_type_name, "virt")) {
+ return VIRT_RPMB;
+ } else {
+ return UNKNOWN_RPMB;
+ }
+}
+
+static const char* _sopts = "hp:d:r:t:";
+static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
+ {"trusty_dev", required_argument, NULL, 'd'},
+ {"data_path", required_argument, NULL, 'p'},
+ {"rpmb_dev", required_argument, NULL, 'r'},
+ {"dev_type", required_argument, NULL, 't'},
+ {0, 0, 0, 0}};
+
+static void show_usage_and_exit(int code) {
+ ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+ ALOGE("Available dev types: mmc, virt\n");
exit(code);
}
-static int drop_privs(void)
-{
+static int drop_privs(void) {
struct __user_cap_header_struct capheader;
struct __user_cap_data_struct capdata[2];
@@ -95,12 +105,10 @@
return 0;
}
-static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
-{
+static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
int rc;
- if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
- (msg->cmd != STORAGE_RPMB_SEND)) {
+ if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
/*
* handling post commit messages on non rpmb commands are not
* implemented as there is no use case for this yet.
@@ -119,42 +127,42 @@
}
switch (msg->cmd) {
- case STORAGE_FILE_DELETE:
- rc = storage_file_delete(msg, req, req_len);
- break;
+ case STORAGE_FILE_DELETE:
+ rc = storage_file_delete(msg, req, req_len);
+ break;
- case STORAGE_FILE_OPEN:
- rc = storage_file_open(msg, req, req_len);
- break;
+ case STORAGE_FILE_OPEN:
+ rc = storage_file_open(msg, req, req_len);
+ break;
- case STORAGE_FILE_CLOSE:
- rc = storage_file_close(msg, req, req_len);
- break;
+ case STORAGE_FILE_CLOSE:
+ rc = storage_file_close(msg, req, req_len);
+ break;
- case STORAGE_FILE_WRITE:
- rc = storage_file_write(msg, req, req_len);
- break;
+ case STORAGE_FILE_WRITE:
+ rc = storage_file_write(msg, req, req_len);
+ break;
- case STORAGE_FILE_READ:
- rc = storage_file_read(msg, req, req_len);
- break;
+ case STORAGE_FILE_READ:
+ rc = storage_file_read(msg, req, req_len);
+ break;
- case STORAGE_FILE_GET_SIZE:
- rc = storage_file_get_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_GET_SIZE:
+ rc = storage_file_get_size(msg, req, req_len);
+ break;
- case STORAGE_FILE_SET_SIZE:
- rc = storage_file_set_size(msg, req, req_len);
- break;
+ case STORAGE_FILE_SET_SIZE:
+ rc = storage_file_set_size(msg, req, req_len);
+ break;
- case STORAGE_RPMB_SEND:
- rc = rpmb_send(msg, req, req_len);
- break;
+ case STORAGE_RPMB_SEND:
+ rc = rpmb_send(msg, req, req_len);
+ break;
- default:
- ALOGE("unhandled command 0x%x\n", msg->cmd);
- msg->result = STORAGE_ERR_UNIMPLEMENTED;
- rc = 1;
+ default:
+ ALOGE("unhandled command 0x%x\n", msg->cmd);
+ msg->result = STORAGE_ERR_UNIMPLEMENTED;
+ rc = 1;
}
if (rc > 0) {
@@ -164,58 +172,58 @@
return rc;
}
-static int proxy_loop(void)
-{
+static int proxy_loop(void) {
ssize_t rc;
struct storage_msg msg;
/* enter main message handling loop */
while (true) {
-
/* get incoming message */
rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
- if (rc < 0)
- return rc;
+ if (rc < 0) return rc;
/* handle request */
req_buffer[rc] = 0; /* force zero termination */
rc = handle_req(&msg, req_buffer, rc);
- if (rc)
- return rc;
+ if (rc) return rc;
}
return 0;
}
-static void parse_args(int argc, char *argv[])
-{
+static void parse_args(int argc, char* argv[]) {
int opt;
int oidx = 0;
while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
switch (opt) {
+ case 'd':
+ trusty_devname = strdup(optarg);
+ break;
- case 'd':
- trusty_devname = strdup(optarg);
- break;
+ case 'p':
+ ss_data_root = strdup(optarg);
+ break;
- case 'p':
- ss_data_root = strdup(optarg);
- break;
+ case 'r':
+ rpmb_devname = strdup(optarg);
+ break;
- case 'r':
- rpmb_devname = strdup(optarg);
- break;
+ case 't':
+ dev_type = parse_dev_type(optarg);
+ if (dev_type == UNKNOWN_RPMB) {
+ ALOGE("Unrecognized dev type: %s\n", optarg);
+ show_usage_and_exit(EXIT_FAILURE);
+ }
+ break;
- default:
- ALOGE("unrecognized option (%c):\n", opt);
- show_usage_and_exit(EXIT_FAILURE);
+ default:
+ ALOGE("unrecognized option (%c):\n", opt);
+ show_usage_and_exit(EXIT_FAILURE);
}
}
- if (ss_data_root == NULL ||
- trusty_devname == NULL ||
- rpmb_devname == NULL) {
+ if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
ALOGE("missing required argument(s)\n");
show_usage_and_exit(EXIT_FAILURE);
}
@@ -226,31 +234,26 @@
ALOGI("rpmb dev: %s\n", rpmb_devname);
}
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
int rc;
/* drop privileges */
- if (drop_privs() < 0)
- return EXIT_FAILURE;
+ if (drop_privs() < 0) return EXIT_FAILURE;
/* parse arguments */
parse_args(argc, argv);
/* initialize secure storage directory */
rc = storage_init(ss_data_root);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* open rpmb device */
- rc = rpmb_open(rpmb_devname);
- if (rc < 0)
- return EXIT_FAILURE;
+ rc = rpmb_open(rpmb_devname, dev_type);
+ if (rc < 0) return EXIT_FAILURE;
/* connect to Trusty secure storage server */
rc = ipc_connect(trusty_devname, ss_srv_name);
- if (rc < 0)
- return EXIT_FAILURE;
+ if (rc < 0) return EXIT_FAILURE;
/* enter main loop */
rc = proxy_loop();
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 9c79105..29827e2 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -51,17 +51,16 @@
static int rpmb_fd = -1;
static uint8_t read_buf[4096];
+static enum dev_type dev_type = UNKNOWN_RPMB;
#ifdef RPMB_DEBUG
-static void print_buf(const char *prefix, const uint8_t *buf, size_t size)
-{
+static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
size_t i;
printf("%s @%p [%zu]", prefix, buf, size);
for (i = 0; i < size; i++) {
- if (i && i % 32 == 0)
- printf("\n%*s", (int) strlen(prefix), "");
+ if (i && i % 32 == 0) printf("\n%*s", (int)strlen(prefix), "");
printf(" %02x", buf[i]);
}
printf("\n");
@@ -70,41 +69,16 @@
#endif
-
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len)
-{
- int rc;
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
struct {
struct mmc_ioc_multi_cmd multi;
struct mmc_ioc_cmd cmd_buf[3];
} mmc = {};
- struct mmc_ioc_cmd *cmd = mmc.multi.cmds;
- const struct storage_rpmb_send_req *req = r;
+ struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
+ int rc;
- if (req_len < sizeof(*req)) {
- ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n",
- req_len, sizeof(*req));
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- size_t expected_len =
- sizeof(*req) + req->reliable_write_size + req->write_size;
- if (req_len != expected_len) {
- ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n",
- req_len, expected_len);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
- const uint8_t *write_buf = req->payload;
+ const uint8_t* write_buf = req->payload;
if (req->reliable_write_size) {
- if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_RELW;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -121,12 +95,6 @@
}
if (req->write_size) {
- if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
- ALOGW("invalid write size %u\n", req->write_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_W;
cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -143,17 +111,9 @@
}
if (req->read_size) {
- if (req->read_size % MMC_BLOCK_SIZE != 0 ||
- req->read_size > sizeof(read_buf)) {
- ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
- msg->result = STORAGE_ERR_NOT_VALID;
- goto err_response;
- }
-
cmd->write_flag = MMC_WRITE_FLAG_R;
cmd->opcode = MMC_READ_MULTIPLE_BLOCK;
- cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC,
- cmd->blksz = MMC_BLOCK_SIZE;
+ cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;
cmd->blocks = req->read_size / MMC_BLOCK_SIZE;
mmc_ioc_cmd_set_data((*cmd), read_buf);
#ifdef RPMB_DEBUG
@@ -163,15 +123,97 @@
cmd++;
}
- rc = ioctl(rpmb_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+ rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
if (rc < 0) {
ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+ }
+ return rc;
+}
+
+static int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,
+ size_t payload_size) {
+ int rc;
+ uint16_t res_count = read_size / MMC_BLOCK_SIZE;
+ uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;
+ rc = write(rpmb_fd, &res_count, sizeof(res_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));
+ if (rc < 0) {
+ return rc;
+ }
+ rc = write(rpmb_fd, payload, payload_size);
+ if (rc < 0) {
+ return rc;
+ }
+ rc = read(rpmb_fd, read_buf, read_size);
+ return rc;
+}
+
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+ int rc;
+ const struct storage_rpmb_send_req* req = r;
+
+ if (req_len < sizeof(*req)) {
+ ALOGW("malformed rpmb request: invalid length (%zu < %zu)\n", req_len, sizeof(*req));
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;
+ if (req_len != expected_len) {
+ ALOGW("malformed rpmb request: invalid length (%zu != %zu)\n", req_len, expected_len);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid reliable write size %u\n", req->reliable_write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if ((req->write_size % MMC_BLOCK_SIZE) != 0) {
+ ALOGW("invalid write size %u\n", req->write_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {
+ ALOGE("%s: invalid read size %u\n", __func__, req->read_size);
+ msg->result = STORAGE_ERR_NOT_VALID;
+ goto err_response;
+ }
+
+ if (dev_type == MMC_RPMB) {
+ rc = send_mmc_rpmb_req(rpmb_fd, req);
+ if (rc < 0) {
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else if (dev_type == VIRT_RPMB) {
+ size_t payload_size = req->reliable_write_size + req->write_size;
+ rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
+ if (rc < 0) {
+ ALOGE("send_virt_rpmb_req failed: %d, %s\n", rc, strerror(errno));
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ if (rc != req->read_size) {
+ ALOGE("send_virt_rpmb_req got incomplete response: "
+ "(size %d, expected %d)\n",
+ rc, req->read_size);
+ msg->result = STORAGE_ERR_GENERIC;
+ goto err_response;
+ }
+ } else {
+ ALOGE("Unsupported dev_type\n");
msg->result = STORAGE_ERR_GENERIC;
goto err_response;
}
#ifdef RPMB_DEBUG
- if (req->read_size)
- print_buf("response: ", read_buf, req->read_size);
+ if (req->read_size) print_buf("response: ", read_buf, req->read_size);
#endif
if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
@@ -188,24 +230,20 @@
return ipc_respond(msg, NULL, 0);
}
-
-int rpmb_open(const char *rpmb_devname)
-{
+int rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {
int rc;
+ dev_type = open_dev_type;
rc = open(rpmb_devname, O_RDWR, 0);
if (rc < 0) {
- ALOGE("unable (%d) to open rpmb device '%s': %s\n",
- errno, rpmb_devname, strerror(errno));
+ ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
return rc;
}
rpmb_fd = rc;
return 0;
}
-void rpmb_close(void)
-{
+void rpmb_close(void) {
close(rpmb_fd);
rpmb_fd = -1;
}
-
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 85cff44..4c330c9 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <trusty/interface/storage.h>
-int rpmb_open(const char *rpmb_devname);
-int rpmb_send(struct storage_msg *msg, const void *r, size_t req_len);
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+
+int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
void rpmb_close(void);
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..fd8daa8 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,9 +19,12 @@
# to pull in the baseline set of Trusty specific modules.
#
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
PRODUCT_PACKAGES += \
- android.hardware.keymaster@3.0-service.trusty \
- gatekeeper.trusty
+ android.hardware.keymaster@4.0-service.trusty \
+ android.hardware.gatekeeper@1.0-service.trusty
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.keystore=trusty \
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
new file mode 100644
index 0000000..77d1f70
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "trusty-ut-ctrl",
+ vendor: true,
+
+ srcs: ["ut-ctrl.c"],
+ shared_libs: [
+ "libc",
+ "liblog",
+ "libtrusty",
+ ],
+ gtest: false,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/trusty/utils/trusty-ut-ctrl/ut-ctrl.c b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
new file mode 100644
index 0000000..9e72af3
--- /dev/null
+++ b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <trusty/tipc.h>
+
+#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
+
+static const char* dev_name = NULL;
+static const char* ut_app = NULL;
+
+static const char* _sopts = "hD:";
+static const struct option _lopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {"dev", required_argument, 0, 'D'},
+ {0, 0, 0, 0},
+};
+
+static const char* usage =
+ "Usage: %s [options] unittest-app\n"
+ "\n"
+ "options:\n"
+ " -h, --help prints this message and exit\n"
+ " -D, --dev name Trusty device name\n"
+ "\n";
+
+static const char* usage_long = "\n";
+
+static bool opt_silent = false;
+
+static void print_usage_and_exit(const char* prog, int code, bool verbose) {
+ fprintf(stderr, usage, prog);
+ if (verbose) {
+ fprintf(stderr, "%s", usage_long);
+ }
+ exit(code);
+}
+
+static void parse_options(int argc, char** argv) {
+ int c;
+ int oidx = 0;
+
+ while (1) {
+ c = getopt_long(argc, argv, _sopts, _lopts, &oidx);
+ if (c == -1) {
+ break; /* done */
+ }
+
+ switch (c) {
+ case 'D':
+ dev_name = strdup(optarg);
+ break;
+
+ case 's':
+ opt_silent = true;
+ break;
+
+ case 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+ break;
+
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+ }
+
+ if (optind < argc) {
+ ut_app = strdup(argv[optind]);
+ }
+}
+
+enum test_message_header {
+ TEST_PASSED = 0,
+ TEST_FAILED = 1,
+ TEST_MESSAGE = 2,
+};
+
+static int run_trusty_unitest(const char* utapp) {
+ int fd;
+ int rc;
+ char rx_buf[1024];
+
+ /* connect to unitest app */
+ fd = tipc_connect(dev_name, utapp);
+ if (fd < 0) {
+ fprintf(stderr, "failed to connect to '%s' app: %s\n", utapp, strerror(-fd));
+ return fd;
+ }
+
+ /* wait for test to complete */
+ for (;;) {
+ rc = read(fd, rx_buf, sizeof(rx_buf));
+ if (rc <= 0 || rc >= (int)sizeof(rx_buf)) {
+ fprintf(stderr, "%s: Read failed: %d\n", __func__, rc);
+ tipc_close(fd);
+ return -1;
+ }
+
+ if (rx_buf[0] == TEST_PASSED) {
+ break;
+ } else if (rx_buf[0] == TEST_FAILED) {
+ break;
+ } else if (rx_buf[0] == TEST_MESSAGE) {
+ write(STDOUT_FILENO, rx_buf + 1, rc - 1);
+ } else {
+ fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
+ break;
+ }
+ }
+
+ /* close connection to unitest app */
+ tipc_close(fd);
+
+ return rx_buf[0] == TEST_PASSED ? 0 : -1;
+}
+
+int main(int argc, char** argv) {
+ int rc = 0;
+
+ if (argc <= 1) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+
+ parse_options(argc, argv);
+
+ if (!dev_name) {
+ dev_name = TIPC_DEFAULT_DEVNAME;
+ }
+
+ if (!ut_app) {
+ fprintf(stderr, "Unittest app must be specified\n");
+ print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+ }
+
+ rc = run_trusty_unitest(ut_app);
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/usbd/usbd.cpp b/usbd/usbd.cpp
index 41cd8dd..6e24d8e 100644
--- a/usbd/usbd.cpp
+++ b/usbd/usbd.cpp
@@ -22,21 +22,25 @@
#include <android-base/properties.h>
#include <android/hardware/usb/gadget/1.0/IUsbGadget.h>
-#define PERSISTENT_USB_CONFIG "persist.sys.usb.config"
+#include <hidl/HidlTransportSupport.h>
using android::base::GetProperty;
using android::base::SetProperty;
+using android::hardware::configureRpcThreadpool;
using android::hardware::usb::gadget::V1_0::GadgetFunction;
using android::hardware::usb::gadget::V1_0::IUsbGadget;
using android::hardware::Return;
int main(int /*argc*/, char** /*argv*/) {
+ if (GetProperty("ro.bootmode", "") == "charger") exit(0);
+
+ configureRpcThreadpool(1, true /*callerWillJoin*/);
android::sp<IUsbGadget> gadget = IUsbGadget::getService();
Return<void> ret;
if (gadget != nullptr) {
LOG(INFO) << "Usb HAL found.";
- std::string function = GetProperty(PERSISTENT_USB_CONFIG, "");
+ std::string function = GetProperty("persist.sys.usb.config", "");
if (function == "adb") {
LOG(INFO) << "peristent prop is adb";
SetProperty("ctl.start", "adbd");