Merge changes from topic "sync-cleanup"
* changes:
sync: remove legacy sync info API
Remove obsolete sync_test.c
diff --git a/adb/Android.bp b/adb/Android.bp
index b9a1596..553473f 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -96,6 +96,7 @@
"adb_io.cpp",
"adb_listeners.cpp",
"adb_trace.cpp",
+ "adb_unique_fd.cpp",
"adb_utils.cpp",
"fdevent.cpp",
"services.cpp",
@@ -122,6 +123,7 @@
"sysdeps_test.cpp",
"sysdeps/stat_test.cpp",
"transport_test.cpp",
+ "types_test.cpp",
]
cc_library_host_static {
@@ -222,7 +224,6 @@
cc_binary_host {
name: "adb",
- tags: ["debug"],
defaults: ["adb_defaults"],
@@ -276,6 +277,7 @@
cc_library_static {
name: "libadbd",
defaults: ["adb_defaults"],
+ recovery_available: true,
// libminadbd wants both, for some reason.
compile_multilib: "both",
@@ -302,6 +304,7 @@
// adbd must be static, as it is copied into the recovery image.
static_executable: true,
+ recovery_available: true,
srcs: [
"daemon/main.cpp",
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 3bf281c..f8a54c6 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -365,8 +365,8 @@
switch (p->msg.arg0) {
#if ADB_HOST
case ADB_AUTH_TOKEN:
- if (t->GetConnectionState() == kCsOffline) {
- t->SetConnectionState(kCsUnauthorized);
+ if (t->GetConnectionState() != kCsAuthorizing) {
+ t->SetConnectionState(kCsAuthorizing);
}
send_auth_response(p->payload.data(), p->msg.data_length, t);
break;
@@ -885,9 +885,8 @@
}
#else /* !defined(_WIN32) */
// set up a pipe so the child can tell us when it is ready.
- // fd[0] will be parent's end, and the child will write on fd[1]
- int fd[2];
- if (pipe(fd)) {
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
return -1;
}
@@ -899,11 +898,10 @@
if (pid == 0) {
// child side of the fork
-
- adb_close(fd[0]);
+ pipe_read.reset();
char reply_fd[30];
- snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
+ snprintf(reply_fd, sizeof(reply_fd), "%d", pipe_write.get());
// child process
int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
"--reply-fd", reply_fd, NULL);
@@ -913,10 +911,10 @@
// parent side of the fork
char temp[3] = {};
// wait for the "OK\n" message
- adb_close(fd[1]);
- int ret = adb_read(fd[0], temp, 3);
+ pipe_write.reset();
+ int ret = adb_read(pipe_read.get(), temp, 3);
int saved_errno = errno;
- adb_close(fd[0]);
+ pipe_read.reset();
if (ret < 0) {
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno);
return -1;
@@ -1103,14 +1101,11 @@
if (!strcmp(service, "reconnect-offline")) {
std::string response;
close_usb_devices([&response](const atransport* transport) {
- switch (transport->GetConnectionState()) {
- case kCsOffline:
- case kCsUnauthorized:
- response += "reconnecting " + transport->serial_name() + "\n";
- return true;
- default:
- return false;
+ if (!ConnectionStateIsOnline(transport->GetConnectionState())) {
+ response += "reconnecting " + transport->serial_name() + "\n";
+ return true;
}
+ return false;
});
if (!response.empty()) {
response.resize(response.size() - 1);
diff --git a/adb/adb.h b/adb/adb.h
index 1e58ee1..ede55da 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -95,16 +95,33 @@
enum ConnectionState {
kCsAny = -1,
- kCsOffline = 0,
+
+ kCsConnecting = 0, // Haven't received a response from the device yet.
+ kCsAuthorizing, // Authorizing with keys from ADB_VENDOR_KEYS.
+ kCsUnauthorized, // ADB_VENDOR_KEYS exhausted, fell back to user prompt.
+ kCsNoPerm, // Insufficient permissions to communicate with the device.
+ kCsOffline,
+
kCsBootloader,
kCsDevice,
kCsHost,
kCsRecovery,
- kCsNoPerm, // Insufficient permissions to communicate with the device.
kCsSideload,
- kCsUnauthorized,
};
+inline bool ConnectionStateIsOnline(ConnectionState state) {
+ switch (state) {
+ case kCsBootloader:
+ case kCsDevice:
+ case kCsHost:
+ case kCsRecovery:
+ case kCsSideload:
+ return true;
+ default:
+ return false;
+ }
+}
+
void print_packet(const char* label, apacket* p);
// These use the system (v)fprintf, not the adb prefixed ones defined in sysdeps.h, so they
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index fecf452..ea5a44e 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -42,7 +42,7 @@
alistener(const std::string& _local_name, const std::string& _connect_to);
~alistener();
- fdevent fde;
+ fdevent* fde = nullptr;
int fd = -1;
std::string local_name;
@@ -60,7 +60,7 @@
alistener::~alistener() {
// Closes the corresponding fd.
- fdevent_remove(&fde);
+ fdevent_destroy(fde);
if (transport) {
transport->RemoveDisconnect(&disconnect);
@@ -222,11 +222,11 @@
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
- fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
+ listener->fde = fdevent_create(listener->fd, ss_listener_event_func, listener.get());
} else {
- fdevent_install(&listener->fde, listener->fd, listener_event_func, listener.get());
+ listener->fde = fdevent_create(listener->fd, listener_event_func, listener.get());
}
- fdevent_set(&listener->fde, FDE_READ);
+ fdevent_set(listener->fde, FDE_READ);
listener->transport = transport;
diff --git a/adb/adb_unique_fd.cpp b/adb/adb_unique_fd.cpp
new file mode 100644
index 0000000..2079be1
--- /dev/null
+++ b/adb/adb_unique_fd.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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_unique_fd.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "sysdeps.h"
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags) {
+ int pipefd[2];
+#if !defined(__APPLE__)
+ if (pipe2(pipefd, flags) != 0) {
+ return false;
+ }
+#else
+ // Darwin doesn't have pipe2. Implement it ourselves.
+ if (flags != 0 && (flags & ~(O_CLOEXEC | O_NONBLOCK)) != 0) {
+ errno = EINVAL;
+ return false;
+ }
+
+ if (pipe(pipefd) != 0) {
+ return false;
+ }
+
+ if (flags & O_CLOEXEC) {
+ if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) != 0 ||
+ fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) != 0) {
+ adb_close(pipefd[0]);
+ adb_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) {
+ adb_close(pipefd[0]);
+ adb_close(pipefd[1]);
+ return false;
+ }
+ }
+#endif
+
+ read->reset(pipefd[0]);
+ write->reset(pipefd[1]);
+ return true;
+}
+#endif
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index 34c1bbc..be63262 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -16,6 +16,9 @@
#pragma once
+#include <errno.h>
+#include <unistd.h>
+
#include <android-base/unique_fd.h>
// Helper to automatically close an FD when it goes out of scope.
@@ -24,3 +27,7 @@
};
using unique_fd = android::base::unique_fd_impl<AdbCloser>;
+
+#if !defined(_WIN32)
+bool Pipe(unique_fd* read, unique_fd* write, int flags = 0);
+#endif
diff --git a/adb/benchmark_device.py b/adb/benchmark_device.py
new file mode 100755
index 0000000..00c2315
--- /dev/null
+++ b/adb/benchmark_device.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+#
+# 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.
+#
+
+import os
+import statistics
+import time
+
+import adb
+
+def lock_min(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo userspace > $x/scaling_governor
+ cat $x/scaling_min_freq > $x/scaling_setspeed
+ done
+ """])
+
+def lock_max(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo userspace > $x/scaling_governor
+ cat $x/scaling_max_freq > $x/scaling_setspeed
+ done
+ """])
+
+def unlock(device):
+ device.shell_nocheck(["""
+ for x in /sys/devices/system/cpu/cpu?/cpufreq; do
+ echo ondemand > $x/scaling_governor
+ echo sched > $x/scaling_governor
+ echo schedutil > $x/scaling_governor
+ done
+ """])
+
+def harmonic_mean(xs):
+ return 1.0 / statistics.mean([1.0 / x for x in xs])
+
+def analyze(name, speeds):
+ median = statistics.median(speeds)
+ mean = harmonic_mean(speeds)
+ stddev = statistics.stdev(speeds)
+ 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_push(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ remote_path = "/dev/null"
+ local_path = "/tmp/adb_benchmark_temp"
+
+ with open(local_path, "wb") as f:
+ f.truncate(file_size_mb * 1024 * 1024)
+
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.push(local=local_path, remote=remote_path)
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("push %dMiB" % file_size_mb, speeds)
+
+def benchmark_pull(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ remote_path = "/data/local/tmp/adb_benchmark_temp"
+ local_path = "/tmp/adb_benchmark_temp"
+
+ device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
+ "count=" + str(file_size_mb)])
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.pull(remote=remote_path, local=local_path)
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("pull %dMiB" % file_size_mb, speeds)
+
+def benchmark_shell(device=None, file_size_mb=100):
+ if device == None:
+ device = adb.get_device()
+
+ lock_max(device)
+
+ speeds = list()
+ for _ in range(0, 5):
+ begin = time.time()
+ device.shell(["dd", "if=/dev/zero", "bs=1m",
+ "count=" + str(file_size_mb)])
+ end = time.time()
+ speeds.append(file_size_mb / float(end - begin))
+
+ analyze("shell %dMiB" % file_size_mb, speeds)
+
+def main():
+ benchmark_pull()
+
+if __name__ == "__main__":
+ main()
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index ade2623..0f4dd33 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -464,6 +464,7 @@
std::shared_ptr<RSA> key = t->NextKey();
if (key == nullptr) {
// No more private keys to try, send the public key.
+ t->SetConnectionState(kCsUnauthorized);
send_auth_publickey(t);
return;
}
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index e476e07..39983ab 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -149,8 +149,8 @@
" emu COMMAND run emulator console command\n"
"\n"
"app installation:\n"
- " install [-lrtsdg] PACKAGE\n"
- " install-multiple [-lrtsdpg] PACKAGE...\n"
+ " install [-lrtsdg] [--instant] PACKAGE\n"
+ " install-multiple [-lrtsdpg] [--instant] PACKAGE...\n"
" push package(s) to the device and install them\n"
" -l: forward lock application\n"
" -r: replace existing application\n"
@@ -159,6 +159,7 @@
" -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"
" uninstall [-k] PACKAGE\n"
" remove this app package from the device\n"
" '-k': keep the data and cache directories\n"
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 31cb853..44ed3a2 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -117,6 +117,7 @@
atexit(adb_server_cleanup);
init_transport_registration();
+ init_reconnect_handler();
init_mdns_transport_discovery();
usb_init();
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 3603f09..283fac5 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -35,7 +35,7 @@
#include "sysdeps.h"
static DNSServiceRef service_ref;
-static fdevent service_ref_fde;
+static fdevent* service_ref_fde;
// Use adb_DNSServiceRefSockFD() instead of calling DNSServiceRefSockFD()
// directly so that the socket is put through the appropriate compatibility
@@ -68,27 +68,26 @@
}
virtual ~AsyncServiceRef() {
- if (! initialized_) {
+ if (!initialized_) {
return;
}
DNSServiceRefDeallocate(sdRef_);
- fdevent_remove(&fde_);
+ fdevent_destroy(fde_);
}
protected:
DNSServiceRef sdRef_;
void Initialize() {
- fdevent_install(&fde_, adb_DNSServiceRefSockFD(sdRef_),
- pump_service_ref, &sdRef_);
- fdevent_set(&fde_, FDE_READ);
+ fde_ = fdevent_create(adb_DNSServiceRefSockFD(sdRef_), pump_service_ref, &sdRef_);
+ fdevent_set(fde_, FDE_READ);
initialized_ = true;
}
private:
- bool initialized_;
- fdevent fde_;
+ bool initialized_ = false;
+ fdevent* fde_;
};
class ResolvedService : public AsyncServiceRef {
@@ -252,14 +251,12 @@
if (errorCode != kDNSServiceErr_NoError) {
D("Got error %d during mDNS browse.", errorCode);
DNSServiceRefDeallocate(sdRef);
- fdevent_remove(&service_ref_fde);
+ fdevent_destroy(service_ref_fde);
return;
}
- auto discovered = new DiscoveredService(interfaceIndex, serviceName,
- regtype, domain);
-
- if (! discovered->Initialized()) {
+ auto discovered = new DiscoveredService(interfaceIndex, serviceName, regtype, domain);
+ if (!discovered->Initialized()) {
delete discovered;
}
}
@@ -274,9 +271,9 @@
}
fdevent_run_on_main_thread([]() {
- fdevent_install(&service_ref_fde, adb_DNSServiceRefSockFD(service_ref), pump_service_ref,
- &service_ref);
- fdevent_set(&service_ref_fde, FDE_READ);
+ service_ref_fde =
+ fdevent_create(adb_DNSServiceRefSockFD(service_ref), pump_service_ref, &service_ref);
+ fdevent_set(service_ref_fde, FDE_READ);
});
}
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 3fd2b31..f0c3629 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -35,8 +35,8 @@
#include <openssl/rsa.h>
#include <openssl/sha.h>
-static fdevent listener_fde;
-static fdevent framework_fde;
+static fdevent* listener_fde = nullptr;
+static fdevent* framework_fde = nullptr;
static int framework_fd = -1;
static void usb_disconnected(void* unused, atransport* t);
@@ -106,8 +106,10 @@
static void framework_disconnected() {
LOG(INFO) << "Framework disconnect";
- fdevent_remove(&framework_fde);
- framework_fd = -1;
+ if (framework_fde) {
+ fdevent_destroy(framework_fde);
+ framework_fd = -1;
+ }
}
static void adbd_auth_event(int fd, unsigned events, void*) {
@@ -168,8 +170,8 @@
}
framework_fd = s;
- fdevent_install(&framework_fde, framework_fd, adbd_auth_event, nullptr);
- fdevent_add(&framework_fde, FDE_READ);
+ framework_fde = fdevent_create(framework_fd, adbd_auth_event, nullptr);
+ fdevent_add(framework_fde, FDE_READ);
if (needs_retry) {
needs_retry = false;
@@ -198,8 +200,8 @@
return;
}
- fdevent_install(&listener_fde, fd, adbd_auth_listener, NULL);
- fdevent_add(&listener_fde, FDE_READ);
+ listener_fde = fdevent_create(fd, adbd_auth_listener, NULL);
+ fdevent_add(listener_fde, FDE_READ);
}
void send_auth_request(atransport* t) {
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index 89577cb..367695d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -139,8 +139,6 @@
fatal("could not create fdevent for new JDWP process");
}
- this->fde->state |= FDE_DONT_CLOSE;
-
/* start by waiting for the PID */
fdevent_add(this->fde, FDE_READ);
}
@@ -148,7 +146,6 @@
~JdwpProcess() {
if (this->socket >= 0) {
adb_shutdown(this->socket);
- adb_close(this->socket);
this->socket = -1;
}
@@ -292,8 +289,6 @@
goto CloseProcess;
}
- adb_close(fd);
-
D("sent file descriptor %d to JDWP process %d", fd, proc->pid);
proc->out_fds.pop_back();
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index d679a6d..830b35d 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -21,18 +21,26 @@
#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/vfs.h>
#include <unistd.h>
+#include <set>
#include <string>
+#include <vector>
#include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
+#include <ext4_utils/ext4_utils.h>
#include "adb.h"
#include "adb_io.h"
+#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "fs_mgr.h"
@@ -82,6 +90,61 @@
return result;
}
+static bool fs_has_shared_blocks(const char* dev) {
+ struct statfs fs;
+ if (statfs(dev, &fs) == -1 || fs.f_type == EXT4_SUPER_MAGIC) {
+ return false;
+ }
+ unique_fd fd(unix_open(dev, O_RDONLY));
+ if (fd < 0) {
+ return false;
+ }
+ struct ext4_super_block sb;
+ if (lseek64(fd, 1024, SEEK_SET) < 0 || unix_read(fd, &sb, sizeof(sb)) < 0) {
+ return false;
+ }
+ struct fs_info info;
+ if (ext4_parse_sb(&sb, &info) < 0) {
+ return false;
+ }
+ return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
+
+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);
+ 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));
+ 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));
+ return false;
+ }
+ if (!WIFEXITED(status)) {
+ WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status);
+ 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);
+ return false;
+ }
+ return true;
+}
+
static bool remount_partition(int fd, const char* dir) {
if (!directory_exists(dir)) {
return true;
@@ -114,17 +177,77 @@
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(int fd, void* cookie) {
+ unique_fd close_fd(fd);
+
+ const char* cmd = reinterpret_cast<const char*>(cookie);
+ bool user_requested_reboot = cmd && !strcmp(cmd, "-R");
+
if (getuid() != 0) {
WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n");
- adb_close(fd);
return;
}
bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
- if (system_verified || vendor_verified) {
+ std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
+ if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+ partitions.push_back("/");
+ } else {
+ partitions.push_back("/system");
+ }
+
+ // Find partitions that are deduplicated, and can be un-deduplicated.
+ std::set<std::string> dedup;
+ for (const auto& partition : partitions) {
+ std::string dev = find_mount(partition.c_str(), partition == "/");
+ if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) {
+ continue;
+ }
+ if (can_unshare_blocks(fd, dev.c_str())) {
+ dedup.emplace(partition);
+ }
+ }
+
+ bool verity_enabled = (system_verified || vendor_verified);
+
+ // 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(fd, nullptr);
+ }
+ reboot_for_remount(fd, !dedup.empty());
+ return;
+ }
+ WriteFdExactly(fd, "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,
@@ -137,20 +260,40 @@
"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,
+ "Alternately, use \"adb remount -R\" to disable verity "
+ "and automatically reboot.\n");
}
bool success = true;
- if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
- success &= remount_partition(fd, "/");
- } else {
- success &= remount_partition(fd, "/system");
+ 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, partition.c_str());
}
- success &= remount_partition(fd, "/odm");
- success &= remount_partition(fd, "/oem");
- success &= remount_partition(fd, "/product");
- success &= remount_partition(fd, "/vendor");
- WriteFdExactly(fd, success ? "remount succeeded\n" : "remount failed\n");
+ if (!dedup.empty()) {
+ WriteFdExactly(fd,
+ "The following partitions are deduplicated and cannot "
+ "yet be remounted:\n");
+ for (const std::string& name : dedup) {
+ WriteFdFmt(fd, " %s\n", name.c_str());
+ }
- adb_close(fd);
+ WriteFdExactly(fd,
+ "To reboot and un-deduplicate the listed partitions, "
+ "please retry with adb remount -R.\n");
+ if (system_verified || vendor_verified) {
+ WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n");
+ }
+ return;
+ }
+
+ if (!success) {
+ WriteFdExactly(fd, "remount failed\n");
+ } else {
+ WriteFdExactly(fd, "remount succeeded\n");
+ }
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index ab11bdd..c724b11 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -234,6 +234,10 @@
for (unsigned i = 0; i < USB_FFS_NUM_BUFS; i++) {
aiob->iocbs[i] = &aiob->iocb[i];
}
+ memset(&aiob->ctx, 0, sizeof(aiob->ctx));
+ if (io_setup(USB_FFS_NUM_BUFS, &aiob->ctx)) {
+ D("[ aio: got error on io_setup (%d) ]", errno);
+ }
}
static int getMaxPacketSize(int ffs_fd) {
@@ -312,13 +316,6 @@
goto err;
}
- memset(&h->read_aiob.ctx, 0, sizeof(h->read_aiob.ctx));
- memset(&h->write_aiob.ctx, 0, sizeof(h->write_aiob.ctx));
- if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
- io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
- D("[ aio: got error on io_setup (%d) ]", errno);
- }
-
h->read_aiob.fd = h->bulk_out;
h->write_aiob.fd = h->bulk_in;
return true;
@@ -439,23 +436,29 @@
num_bufs += 1;
}
- if (io_submit(aiob->ctx, num_bufs, aiob->iocbs.data()) < num_bufs) {
- D("[ aio: got error submitting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- if (TEMP_FAILURE_RETRY(
- io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(), nullptr)) < num_bufs) {
- D("[ aio: got error waiting %s (%d) ]", read ? "read" : "write", errno);
- return -1;
- }
- for (int i = 0; i < num_bufs; i++) {
- if (aiob->events[i].res < 0) {
- errno = aiob->events[i].res;
- D("[ aio: got error event on %s (%d) ]", read ? "read" : "write", errno);
+ while (true) {
+ if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {
+ PLOG(ERROR) << "aio: got error submitting " << (read ? "read" : "write");
return -1;
}
+ if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),
+ nullptr)) < num_bufs) {
+ PLOG(ERROR) << "aio: got error waiting " << (read ? "read" : "write");
+ return -1;
+ }
+ if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
+ continue;
+ }
+ for (int i = 0; i < num_bufs; i++) {
+ if (aiob->events[i].res < 0) {
+ errno = -aiob->events[i].res;
+ PLOG(ERROR) << "aio: got error event on " << (read ? "read" : "write")
+ << " total bufs " << num_bufs;
+ return -1;
+ }
+ }
+ return 0;
}
- return 0;
}
static int usb_ffs_aio_read(usb_handle* h, void* data, int len) {
@@ -494,8 +497,6 @@
h->kicked = false;
adb_close(h->bulk_out);
adb_close(h->bulk_in);
- io_destroy(h->read_aiob.ctx);
- io_destroy(h->write_aiob.ctx);
// Notify usb_adb_open_thread to open a new connection.
h->lock.lock();
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index cf441cf..1b7758c 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -57,7 +57,7 @@
explicit PollNode(fdevent* fde) : fde(fde) {
memset(&pollfd, 0, sizeof(pollfd));
- pollfd.fd = fde->fd;
+ pollfd.fd = fde->fd.get();
#if defined(__linux__)
// Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
@@ -111,37 +111,22 @@
if (fde->state & FDE_ERROR) {
state += "E";
}
- if (fde->state & FDE_DONT_CLOSE) {
- state += "D";
- }
- return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
- check_main_thread();
- fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
- if(fde == 0) return 0;
- fdevent_install(fde, fd, func, arg);
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-void fdevent_destroy(fdevent* fde) {
- check_main_thread();
- if(fde == 0) return;
- if(!(fde->state & FDE_CREATED)) {
- LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
- }
- fdevent_remove(fde);
- free(fde);
+ return android::base::StringPrintf("(fdevent %d %s)", 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->state = FDE_ACTIVE;
- fde->fd = fd;
+ fde->fd.reset(fd);
fde->func = func;
fde->arg = arg;
if (!set_file_block_mode(fd, false)) {
@@ -150,30 +135,35 @@
// to handle it.
LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
}
- auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+ auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
CHECK(pair.second) << "install existing fd " << fd;
- D("fdevent_install %s", dump_fde(fde).c_str());
+
+ fde->state |= FDE_CREATED;
+ return fde;
}
-void fdevent_remove(fdevent* fde) {
+void fdevent_destroy(fdevent* fde) {
check_main_thread();
- D("fdevent_remove %s", dump_fde(fde).c_str());
+ if (fde == 0) return;
+ if (!(fde->state & FDE_CREATED)) {
+ LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
+ }
+
if (fde->state & FDE_ACTIVE) {
- g_poll_node_map.erase(fde->fd);
+ g_poll_node_map.erase(fde->fd.get());
if (fde->state & FDE_PENDING) {
g_pending_list.remove(fde);
}
- if (!(fde->state & FDE_DONT_CLOSE)) {
- adb_close(fde->fd);
- fde->fd = -1;
- }
+ fde->fd.reset();
fde->state = 0;
fde->events = 0;
}
+
+ delete fde;
}
static void fdevent_update(fdevent* fde, unsigned events) {
- auto it = g_poll_node_map.find(fde->fd);
+ 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) {
@@ -272,7 +262,7 @@
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, pollfd.fd);
+ 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;
@@ -287,7 +277,7 @@
CHECK(fde->state & FDE_PENDING);
fde->state &= (~FDE_PENDING);
D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
- fde->func(fde->fd, events, fde->arg);
+ fde->func(fde->fd.get(), events, fde->arg);
}
static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 896400a..69c4072 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -22,28 +22,27 @@
#include <functional>
+#include "adb_unique_fd.h"
+
/* events that may be observed */
#define FDE_READ 0x0001
#define FDE_WRITE 0x0002
#define FDE_ERROR 0x0004
-/* features that may be set (via the events set/add/del interface) */
-#define FDE_DONT_CLOSE 0x0080
-
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
struct fdevent {
- fdevent *next;
- fdevent *prev;
+ fdevent* next = nullptr;
+ fdevent* prev = nullptr;
- int fd;
- int force_eof;
+ unique_fd fd;
+ int force_eof = 0;
- uint16_t state;
- uint16_t events;
+ uint16_t state = 0;
+ uint16_t events = 0;
- fd_func func;
- void *arg;
+ fd_func func = nullptr;
+ void* arg = nullptr;
};
/* Allocate and initialize a new fdevent object
@@ -57,15 +56,6 @@
*/
void fdevent_destroy(fdevent *fde);
-/* Initialize an fdevent object that was externally allocated
-*/
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-
-/* Uninitialize an fdevent object that was initialized by
-** fdevent_install()
-*/
-void fdevent_remove(fdevent *item);
-
/* Change which events should cause notifications
*/
void fdevent_set(fdevent *fde, unsigned events);
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 2f0ff18..0cb2439 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -31,14 +31,14 @@
class FdHandler {
public:
FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
- fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
- fdevent_add(&read_fde_, FDE_READ);
- fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+ read_fde_ = fdevent_create(read_fd_, FdEventCallback, this);
+ fdevent_add(read_fde_, FDE_READ);
+ write_fde_ = fdevent_create(write_fd_, FdEventCallback, this);
}
~FdHandler() {
- fdevent_remove(&read_fde_);
- fdevent_remove(&write_fde_);
+ fdevent_destroy(read_fde_);
+ fdevent_destroy(write_fde_);
}
private:
@@ -50,7 +50,7 @@
char c;
ASSERT_EQ(1, adb_read(fd, &c, 1));
handler->queue_.push(c);
- fdevent_add(&handler->write_fde_, FDE_WRITE);
+ fdevent_add(handler->write_fde_, FDE_WRITE);
}
if (events & FDE_WRITE) {
ASSERT_EQ(fd, handler->write_fd_);
@@ -59,7 +59,7 @@
handler->queue_.pop();
ASSERT_EQ(1, adb_write(fd, &c, 1));
if (handler->queue_.empty()) {
- fdevent_del(&handler->write_fde_, FDE_WRITE);
+ fdevent_del(handler->write_fde_, FDE_WRITE);
}
}
}
@@ -67,8 +67,8 @@
private:
const int read_fd_;
const int write_fd_;
- fdevent read_fde_;
- fdevent write_fde_;
+ fdevent* read_fde_;
+ fdevent* write_fde_;
std::queue<char> queue_;
};
@@ -137,7 +137,7 @@
}
struct InvalidFdArg {
- fdevent fde;
+ fdevent* fde;
unsigned expected_events;
size_t* happened_event_count;
};
@@ -145,7 +145,7 @@
static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
ASSERT_EQ(arg->expected_events, events);
- fdevent_remove(&arg->fde);
+ fdevent_destroy(arg->fde);
if (++*(arg->happened_event_count) == 2) {
fdevent_terminate_loop();
}
@@ -157,15 +157,15 @@
InvalidFdArg read_arg;
read_arg.expected_events = FDE_READ | FDE_ERROR;
read_arg.happened_event_count = &happened_event_count;
- fdevent_install(&read_arg.fde, INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
- fdevent_add(&read_arg.fde, FDE_READ);
+ 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;
- fdevent_install(&write_arg.fde, INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
- fdevent_add(&write_arg.fde, FDE_WRITE);
+ write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+ fdevent_add(write_arg.fde, FDE_WRITE);
fdevent_loop();
}
diff --git a/adb/services.cpp b/adb/services.cpp
index 0b0c161..a757d90 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -291,7 +291,9 @@
} else if(!strncmp(name, "sync:", 5)) {
ret = create_service_thread("sync", file_sync_service, nullptr);
} else if(!strncmp(name, "remount:", 8)) {
- ret = create_service_thread("remount", remount_service, nullptr);
+ const char* options = name + strlen("remount:");
+ void* cookie = const_cast<void*>(reinterpret_cast<const void*>(options));
+ ret = create_service_thread("remount", remount_service, cookie);
} else if(!strncmp(name, "reboot:", 7)) {
void* arg = strdup(name + 7);
if (arg == NULL) return -1;
diff --git a/adb/socket.h b/adb/socket.h
index e0fd87f..0905aab 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -58,11 +58,11 @@
* us to our fd event system. For remote asockets
* these fields are not used.
*/
- fdevent fde = {};
- int fd = 0;
+ fdevent* fde = nullptr;
+ int fd = -1;
// queue of data waiting to be written
- std::deque<Range> packet_queue;
+ IOVector packet_queue;
std::string smart_socket_data;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 7bc0165..de3215d 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -113,24 +113,23 @@
};
static SocketFlushResult local_socket_flush_incoming(asocket* s) {
- while (!s->packet_queue.empty()) {
- Range& r = s->packet_queue.front();
-
- int rc = adb_write(s->fd, r.data(), r.size());
- if (rc == static_cast<int>(r.size())) {
- s->packet_queue.pop_front();
+ if (!s->packet_queue.empty()) {
+ std::vector<adb_iovec> iov = s->packet_queue.iovecs();
+ ssize_t rc = adb_writev(s->fd, iov.data(), iov.size());
+ if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
+ s->packet_queue.clear();
} else if (rc > 0) {
- r.drop_front(rc);
- fdevent_add(&s->fde, FDE_WRITE);
+ // TODO: Implement a faster drop_front?
+ s->packet_queue.take_front(rc);
+ fdevent_add(s->fde, FDE_WRITE);
return SocketFlushResult::TryAgain;
} else if (rc == -1 && errno == EAGAIN) {
- fdevent_add(&s->fde, FDE_WRITE);
+ fdevent_add(s->fde, FDE_WRITE);
return SocketFlushResult::TryAgain;
} else {
// We failed to write, but it's possible that we can still read from the socket.
// Give that a try before giving up.
s->has_write_error = true;
- break;
}
}
@@ -140,7 +139,7 @@
return SocketFlushResult::Destroyed;
}
- fdevent_del(&s->fde, FDE_WRITE);
+ fdevent_del(s->fde, FDE_WRITE);
return SocketFlushResult::Completed;
}
@@ -173,7 +172,7 @@
break;
}
D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
- s->fde.force_eof);
+ s->fde->force_eof);
if (avail != max_payload && s->peer) {
data.resize(max_payload - avail);
@@ -200,13 +199,13 @@
** we disable notification of READs. They'll
** be enabled again when we get a call to ready()
*/
- fdevent_del(&s->fde, FDE_READ);
+ fdevent_del(s->fde, FDE_READ);
}
}
// Don't allow a forced eof if data is still there.
- if ((s->fde.force_eof && !r) || is_eof) {
- D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
+ if ((s->fde->force_eof && !r) || is_eof) {
+ D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde->force_eof);
s->close(s);
return false;
}
@@ -217,8 +216,7 @@
static int local_socket_enqueue(asocket* s, apacket::payload_type data) {
D("LS(%d): enqueue %zu", s->id, data.size());
- Range r(std::move(data));
- s->packet_queue.push_back(std::move(r));
+ s->packet_queue.append(std::move(data));
switch (local_socket_flush_incoming(s)) {
case SocketFlushResult::Destroyed:
return -1;
@@ -236,19 +234,19 @@
static void local_socket_ready(asocket* s) {
/* far side is ready for data, pay attention to
readable events */
- fdevent_add(&s->fde, FDE_READ);
+ fdevent_add(s->fde, FDE_READ);
}
// 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->fde.fd);
+ D("LS(%d): destroying fde.fd=%d", s->id, s->fd);
/* IMPORTANT: the remove closes the fd
** that belongs to this socket
*/
- fdevent_remove(&s->fde);
+ fdevent_destroy(s->fde);
remove_socket(s);
delete s;
@@ -290,11 +288,11 @@
*/
D("LS(%d): closing", s->id);
s->closing = 1;
- fdevent_del(&s->fde, FDE_READ);
+ fdevent_del(s->fde, FDE_READ);
remove_socket(s);
D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
local_socket_closing_list.push_back(s);
- CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
+ CHECK_EQ(FDE_WRITE, s->fde->state & FDE_WRITE);
}
static void local_socket_event_func(int fd, unsigned ev, void* _s) {
@@ -343,7 +341,7 @@
s->close = local_socket_close;
install_local_socket(s);
- fdevent_install(&s->fde, fd, local_socket_event_func, s);
+ s->fde = fdevent_create(fd, local_socket_event_func, s);
D("LS(%d): created (fd=%d)", s->id, s->fd);
return s;
}
@@ -622,7 +620,7 @@
D("SS(%d): enqueue %zu", s->id, data.size());
if (s->smart_socket_data.empty()) {
- // TODO: Make this a BlockChain?
+ // TODO: Make this an IOVector?
s->smart_socket_data.assign(data.begin(), data.end());
} else {
std::copy(data.begin(), data.end(), std::back_inserter(s->smart_socket_data));
@@ -750,7 +748,7 @@
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
goto fail;
- } else if (s->transport->GetConnectionState() == kCsOffline) {
+ } else if (!ConnectionStateIsOnline(s->transport->GetConnectionState())) {
/* if there's no remote we fail the connection
** right here and terminate it
*/
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 32bf029..ddd3ff0 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -36,10 +36,11 @@
@contextlib.contextmanager
-def fake_adb_server(protocol=socket.AF_INET, port=0):
- """Creates a fake ADB server that just replies with a CNXN packet."""
+def fake_adbd(protocol=socket.AF_INET, port=0):
+ """Creates a fake ADB daemon that just replies with a CNXN packet."""
serversock = socket.socket(protocol, socket.SOCK_STREAM)
+ serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if protocol == socket.AF_INET:
serversock.bind(('127.0.0.1', port))
else:
@@ -60,31 +61,33 @@
rlist = [readpipe, serversock]
cnxn_sent = {}
while True:
- ready, _, _ = select.select(rlist, [], [])
- for r in ready:
- if r == readpipe:
+ read_ready, _, _ = select.select(rlist, [], [])
+ for ready in read_ready:
+ if ready == readpipe:
# Closure pipe
- os.close(r)
+ os.close(ready)
serversock.shutdown(socket.SHUT_RDWR)
serversock.close()
return
- elif r == serversock:
+ elif ready == serversock:
# Server socket
- conn, _ = r.accept()
+ conn, _ = ready.accept()
rlist.append(conn)
else:
# Client socket
- data = r.recv(1024)
- if not data:
- if r in cnxn_sent:
- del cnxn_sent[r]
- rlist.remove(r)
+ data = ready.recv(1024)
+ if not data or data.startswith('OPEN'):
+ if ready in cnxn_sent:
+ del cnxn_sent[ready]
+ ready.shutdown(socket.SHUT_RDWR)
+ ready.close()
+ rlist.remove(ready)
continue
- if r in cnxn_sent:
+ if ready in cnxn_sent:
continue
- cnxn_sent[r] = True
- r.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
- 'device::ro.product.name=fakeadb'))
+ cnxn_sent[ready] = True
+ ready.sendall(_adb_packet('CNXN', 0x01000001, 1024 * 1024,
+ 'device::ro.product.name=fakeadb'))
port = serversock.getsockname()[1]
server_thread = threading.Thread(target=_handle)
@@ -97,8 +100,52 @@
server_thread.join()
-class NonApiTest(unittest.TestCase):
- """Tests for ADB that aren't a part of the AndroidDevice API."""
+@contextlib.contextmanager
+def adb_connect(unittest, serial):
+ """Context manager for an ADB connection.
+
+ This automatically disconnects when done with the connection.
+ """
+
+ output = subprocess.check_output(['adb', 'connect', serial])
+ unittest.assertEqual(output.strip(), 'connected to {}'.format(serial))
+
+ try:
+ yield
+ finally:
+ # Perform best-effort disconnection. Discard the output.
+ subprocess.Popen(['adb', 'disconnect', serial],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+
+
+@contextlib.contextmanager
+def adb_server():
+ """Context manager for an ADB server.
+
+ This creates an ADB server and returns the port it's listening on.
+ """
+
+ port = 5038
+ # Kill any existing server on this non-default port.
+ subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+ stderr=subprocess.STDOUT)
+ read_pipe, write_pipe = os.pipe()
+ proc = subprocess.Popen(['adb', '-L', 'tcp:localhost:{}'.format(port),
+ 'fork-server', 'server',
+ '--reply-fd', str(write_pipe)])
+ try:
+ os.close(write_pipe)
+ greeting = os.read(read_pipe, 1024)
+ assert greeting == 'OK\n', repr(greeting)
+ yield port
+ finally:
+ proc.terminate()
+ proc.wait()
+
+
+class CommandlineTest(unittest.TestCase):
+ """Tests for the ADB commandline."""
def test_help(self):
"""Make sure we get _something_ out of help."""
@@ -120,28 +167,37 @@
revision_line, r'^Revision [0-9a-f]{12}-android$')
def test_tcpip_error_messages(self):
- p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- self.assertEqual(1, p.returncode)
+ """Make sure 'adb tcpip' parsing is sane."""
+ proc = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = proc.communicate()
+ self.assertEqual(1, proc.returncode)
self.assertIn('requires an argument', out)
- p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- out, _ = p.communicate()
- self.assertEqual(1, p.returncode)
+ proc = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, _ = proc.communicate()
+ self.assertEqual(1, proc.returncode)
self.assertIn('invalid port', out)
- # Helper method that reads a pipe until it is closed, then sets the event.
- def _read_pipe_and_set_event(self, pipe, event):
- x = pipe.read()
+
+class ServerTest(unittest.TestCase):
+ """Tests for the ADB server."""
+
+ @staticmethod
+ def _read_pipe_and_set_event(pipe, event):
+ """Reads a pipe until it is closed, then sets the event."""
+ pipe.read()
event.set()
- # Test that launch_server() does not let the adb server inherit
- # stdin/stdout/stderr handles which can cause callers of adb.exe to hang.
- # This test also runs fine on unix even though the impetus is an issue
- # unique to Windows.
def test_handle_inheritance(self):
+ """Test that launch_server() does not inherit handles.
+
+ launch_server() should not let the adb server inherit
+ stdin/stdout/stderr handles, which can cause callers of adb.exe to hang.
+ This test also runs fine on unix even though the impetus is an issue
+ unique to Windows.
+ """
# This test takes 5 seconds to run on Windows: if there is no adb server
# running on the the port used below, adb kill-server tries to make a
# TCP connection to a closed port and that takes 1 second on Windows;
@@ -163,29 +219,30 @@
try:
# Run the adb client and have it start the adb server.
- p = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ proc = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
# Start threads that set events when stdout/stderr are closed.
stdout_event = threading.Event()
stdout_thread = threading.Thread(
- target=self._read_pipe_and_set_event,
- args=(p.stdout, stdout_event))
+ target=ServerTest._read_pipe_and_set_event,
+ args=(proc.stdout, stdout_event))
stdout_thread.daemon = True
stdout_thread.start()
stderr_event = threading.Event()
stderr_thread = threading.Thread(
- target=self._read_pipe_and_set_event,
- args=(p.stderr, stderr_event))
+ target=ServerTest._read_pipe_and_set_event,
+ args=(proc.stderr, stderr_event))
stderr_thread.daemon = True
stderr_thread.start()
# Wait for the adb client to finish. Once that has occurred, if
# stdin/stderr/stdout are still open, it must be open in the adb
# server.
- p.wait()
+ proc.wait()
# Try to write to stdin which we expect is closed. If it isn't
# closed, we should get an IOError. If we don't get an IOError,
@@ -193,7 +250,7 @@
# probably letting the adb server inherit stdin which would be
# wrong.
with self.assertRaises(IOError):
- p.stdin.write('x')
+ proc.stdin.write('x')
# Wait a few seconds for stdout/stderr to be closed (in the success
# case, this won't wait at all). If there is a timeout, that means
@@ -207,8 +264,12 @@
subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
stderr=subprocess.STDOUT)
- # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+
+class EmulatorTest(unittest.TestCase):
+ """Tests for the emulator connection."""
+
def _reset_socket_on_close(self, sock):
+ """Use SO_LINGER to cause TCP RST segment to be sent on socket close."""
# The linger structure is two shorts on Windows, but two ints on Unix.
linger_format = 'hh' if os.name == 'nt' else 'ii'
l_onoff = 1
@@ -227,7 +288,7 @@
Bug: https://code.google.com/p/android/issues/detail?id=21021
"""
with contextlib.closing(
- socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
# Use SO_REUSEADDR so subsequent runs of the test can grab the port
# even if it is in TIME_WAIT.
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -237,7 +298,7 @@
# Now that listening has started, start adb emu kill, telling it to
# connect to our mock emulator.
- p = subprocess.Popen(
+ proc = subprocess.Popen(
['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
stderr=subprocess.STDOUT)
@@ -246,12 +307,16 @@
# If WSAECONNABORTED (10053) is raised by any socket calls,
# then adb probably isn't reading the data that we sent it.
conn.sendall('Android Console: type \'help\' for a list ' +
- 'of commands\r\n')
+ 'of commands\r\n')
conn.sendall('OK\r\n')
- with contextlib.closing(conn.makefile()) as f:
- self.assertEqual('kill\n', f.readline())
- self.assertEqual('quit\n', f.readline())
+ with contextlib.closing(conn.makefile()) as connf:
+ line = connf.readline()
+ if line.startswith('auth'):
+ # Ignore the first auth line.
+ line = connf.readline()
+ self.assertEqual('kill\n', line)
+ self.assertEqual('quit\n', connf.readline())
conn.sendall('OK: killing emulator, bye bye\r\n')
@@ -264,11 +329,48 @@
self._reset_socket_on_close(conn)
# Wait for adb to finish, so we can check return code.
- p.communicate()
+ proc.communicate()
# If this fails, adb probably isn't ignoring WSAECONNRESET when
# reading the response from the adb emu kill command (on Windows).
- self.assertEqual(0, p.returncode)
+ self.assertEqual(0, proc.returncode)
+
+ def test_emulator_connect(self):
+ """Ensure that the emulator can connect.
+
+ Bug: http://b/78991667
+ """
+ with adb_server() as server_port:
+ with fake_adbd() as port:
+ serial = 'emulator-{}'.format(port - 1)
+ # Ensure that the emulator is not there.
+ try:
+ subprocess.check_output(['adb', '-P', str(server_port),
+ '-s', serial, 'get-state'],
+ stderr=subprocess.STDOUT)
+ self.fail('Device should not be available')
+ except subprocess.CalledProcessError as err:
+ self.assertEqual(
+ err.output.strip(),
+ 'error: device \'{}\' not found'.format(serial))
+
+ # Let the ADB server know that the emulator has started.
+ with contextlib.closing(
+ socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
+ sock.connect(('localhost', server_port))
+ command = 'host:emulator:{}'.format(port)
+ sock.sendall('%04x%s' % (len(command), command))
+
+ # Ensure the emulator is there.
+ subprocess.check_call(['adb', '-P', str(server_port),
+ '-s', serial, 'wait-for-device'])
+ output = subprocess.check_output(['adb', '-P', str(server_port),
+ '-s', serial, 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+
+class ConnectionTest(unittest.TestCase):
+ """Tests for adb connect."""
def test_connect_ipv4_ipv6(self):
"""Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
@@ -277,38 +379,67 @@
"""
for protocol in (socket.AF_INET, socket.AF_INET6):
try:
- with fake_adb_server(protocol=protocol) as port:
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
-
- self.assertEqual(
- output.strip(), 'connected to localhost:{}'.format(port))
+ with fake_adbd(protocol=protocol) as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ pass
except socket.error:
print("IPv6 not available, skipping")
continue
def test_already_connected(self):
- with fake_adb_server() as port:
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
+ """Ensure that an already-connected device stays connected."""
- self.assertEqual(
- output.strip(), 'connected to localhost:{}'.format(port))
+ with fake_adbd() as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ # b/31250450: this always returns 0 but probably shouldn't.
+ output = subprocess.check_output(['adb', 'connect', serial])
+ self.assertEqual(
+ output.strip(), 'already connected to {}'.format(serial))
- # b/31250450: this always returns 0 but probably shouldn't.
- output = subprocess.check_output(
- ['adb', 'connect', 'localhost:{}'.format(port)])
+ def test_reconnect(self):
+ """Ensure that a disconnected device reconnects."""
- self.assertEqual(
- output.strip(), 'already connected to localhost:{}'.format(port))
+ with fake_adbd() as port:
+ serial = 'localhost:{}'.format(port)
+ with adb_connect(self, serial):
+ output = subprocess.check_output(['adb', '-s', serial,
+ 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+ # This will fail.
+ proc = subprocess.Popen(['adb', '-s', serial, 'shell', 'true'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output, _ = proc.communicate()
+ self.assertEqual(output.strip(), 'error: closed')
+
+ subprocess.check_call(['adb', '-s', serial, 'wait-for-device'])
+
+ output = subprocess.check_output(['adb', '-s', serial,
+ 'get-state'])
+ self.assertEqual(output.strip(), 'device')
+
+ # Once we explicitly kick a device, it won't attempt to
+ # reconnect.
+ output = subprocess.check_output(['adb', 'disconnect', serial])
+ self.assertEqual(
+ output.strip(), 'disconnected {}'.format(serial))
+ try:
+ subprocess.check_output(['adb', '-s', serial, 'get-state'],
+ stderr=subprocess.STDOUT)
+ self.fail('Device should not be available')
+ except subprocess.CalledProcessError as err:
+ self.assertEqual(
+ err.output.strip(),
+ 'error: device \'{}\' not found'.format(serial))
+
def main():
+ """Main entrypoint."""
random.seed(0)
- if len(adb.get_devices()) > 0:
- suite = unittest.TestLoader().loadTestsFromName(__name__)
- unittest.TextTestRunner(verbosity=3).run(suite)
- else:
- print('Test suite must be run with attached devices')
+ unittest.main(verbosity=3)
if __name__ == '__main__':
diff --git a/adb/transport.cpp b/adb/transport.cpp
index fa7cc8c..7db9bf2 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -33,6 +33,7 @@
#include <deque>
#include <list>
#include <mutex>
+#include <queue>
#include <thread>
#include <android-base/logging.h>
@@ -50,7 +51,9 @@
#include "adb_utils.h"
#include "fdevent.h"
-static void transport_unref(atransport *t);
+static void register_transport(atransport* transport);
+static void remove_transport(atransport* transport);
+static void transport_unref(atransport* transport);
// TODO: unordered_map<TransportId, atransport*>
static auto& transport_list = *new std::list<atransport*>();
@@ -77,6 +80,130 @@
~ScopedAssumeLocked() RELEASE() {}
};
+// Tracks and handles atransport*s that are attempting reconnection.
+class ReconnectHandler {
+ public:
+ ReconnectHandler() = default;
+ ~ReconnectHandler() = default;
+
+ // Starts the ReconnectHandler thread.
+ void Start();
+
+ // Requests the ReconnectHandler thread to stop.
+ void Stop();
+
+ // Adds the atransport* to the queue of reconnect attempts.
+ void TrackTransport(atransport* transport);
+
+ private:
+ // The main thread loop.
+ void Run();
+
+ // Tracks a reconnection attempt.
+ struct ReconnectAttempt {
+ atransport* transport;
+ std::chrono::system_clock::time_point deadline;
+ size_t attempts_left;
+ };
+
+ // Only retry for up to one minute.
+ static constexpr const std::chrono::seconds kDefaultTimeout = std::chrono::seconds(10);
+ static constexpr const size_t kMaxAttempts = 6;
+
+ // Protects all members.
+ std::mutex reconnect_mutex_;
+ bool running_ GUARDED_BY(reconnect_mutex_) = true;
+ std::thread handler_thread_;
+ std::condition_variable reconnect_cv_;
+ std::queue<ReconnectAttempt> reconnect_queue_ GUARDED_BY(reconnect_mutex_);
+
+ DISALLOW_COPY_AND_ASSIGN(ReconnectHandler);
+};
+
+void ReconnectHandler::Start() {
+ check_main_thread();
+ handler_thread_ = std::thread(&ReconnectHandler::Run, this);
+}
+
+void ReconnectHandler::Stop() {
+ check_main_thread();
+ {
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ running_ = false;
+ }
+ reconnect_cv_.notify_one();
+ handler_thread_.join();
+
+ // Drain the queue to free all resources.
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ while (!reconnect_queue_.empty()) {
+ ReconnectAttempt attempt = reconnect_queue_.front();
+ reconnect_queue_.pop();
+ remove_transport(attempt.transport);
+ }
+}
+
+void ReconnectHandler::TrackTransport(atransport* transport) {
+ check_main_thread();
+ {
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ if (!running_) return;
+ reconnect_queue_.emplace(ReconnectAttempt{
+ transport, std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+ ReconnectHandler::kMaxAttempts});
+ }
+ reconnect_cv_.notify_one();
+}
+
+void ReconnectHandler::Run() {
+ while (true) {
+ ReconnectAttempt attempt;
+ {
+ std::unique_lock<std::mutex> lock(reconnect_mutex_);
+ ScopedAssumeLocked assume_lock(reconnect_mutex_);
+
+ auto deadline = std::chrono::time_point<std::chrono::system_clock>::max();
+ if (!reconnect_queue_.empty()) deadline = reconnect_queue_.front().deadline;
+ reconnect_cv_.wait_until(lock, deadline, [&]() REQUIRES(reconnect_mutex_) {
+ return !running_ ||
+ (!reconnect_queue_.empty() && reconnect_queue_.front().deadline < deadline);
+ });
+
+ if (!running_) return;
+ attempt = reconnect_queue_.front();
+ reconnect_queue_.pop();
+ if (attempt.transport->kicked()) {
+ D("transport %s was kicked. giving up on it.", attempt.transport->serial);
+ remove_transport(attempt.transport);
+ continue;
+ }
+ }
+ D("attempting to reconnect %s", attempt.transport->serial);
+
+ if (!attempt.transport->Reconnect()) {
+ D("attempting to reconnect %s failed.", attempt.transport->serial);
+ if (attempt.attempts_left == 0) {
+ D("transport %s exceeded the number of retry attempts. giving up on it.",
+ attempt.transport->serial);
+ remove_transport(attempt.transport);
+ continue;
+ }
+
+ std::lock_guard<std::mutex> lock(reconnect_mutex_);
+ reconnect_queue_.emplace(ReconnectAttempt{
+ attempt.transport,
+ std::chrono::system_clock::now() + ReconnectHandler::kDefaultTimeout,
+ attempt.attempts_left - 1});
+ continue;
+ }
+
+ D("reconnection to %s succeeded.", attempt.transport->serial);
+ register_transport(attempt.transport);
+ }
+}
+
+static auto& reconnect_handler = *new ReconnectHandler();
+
} // namespace
TransportId NextTransportId() {
@@ -300,7 +427,7 @@
static int transport_registration_send = -1;
static int transport_registration_recv = -1;
-static fdevent transport_registration_fde;
+static fdevent* transport_registration_fde;
#if ADB_HOST
@@ -477,8 +604,6 @@
return 0;
}
-static void remove_transport(atransport*);
-
static void transport_registration_func(int _fd, unsigned ev, void*) {
tmsg m;
atransport* t;
@@ -515,8 +640,9 @@
/* don't create transport threads for inaccessible devices */
if (t->GetConnectionState() != kCsNoPerm) {
- /* initial references are the two threads */
- t->ref_count = 1;
+ // The connection gets a reference to the atransport. It will release it
+ // upon a read/write error.
+ t->ref_count++;
t->connection()->SetTransportName(t->serial_name());
t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
if (!check_header(p.get(), t)) {
@@ -547,13 +673,20 @@
{
std::lock_guard<std::recursive_mutex> lock(transport_lock);
- pending_list.remove(t);
- transport_list.push_front(t);
+ auto it = std::find(pending_list.begin(), pending_list.end(), t);
+ if (it != pending_list.end()) {
+ pending_list.remove(t);
+ transport_list.push_front(t);
+ }
}
update_transports();
}
+void init_reconnect_handler(void) {
+ reconnect_handler.Start();
+}
+
void init_transport_registration(void) {
int s[2];
@@ -565,13 +698,13 @@
transport_registration_send = s[0];
transport_registration_recv = s[1];
- fdevent_install(&transport_registration_fde, transport_registration_recv,
- transport_registration_func, 0);
-
- fdevent_set(&transport_registration_fde, FDE_READ);
+ transport_registration_fde =
+ fdevent_create(transport_registration_recv, transport_registration_func, 0);
+ fdevent_set(transport_registration_fde, FDE_READ);
}
void kick_all_transports() {
+ reconnect_handler.Stop();
// To avoid only writing part of a packet to a transport after exit, kick all transports.
std::lock_guard<std::recursive_mutex> lock(transport_lock);
for (auto t : transport_list) {
@@ -601,15 +734,21 @@
}
static void transport_unref(atransport* t) {
+ check_main_thread();
CHECK(t != nullptr);
std::lock_guard<std::recursive_mutex> lock(transport_lock);
CHECK_GT(t->ref_count, 0u);
t->ref_count--;
if (t->ref_count == 0) {
- D("transport: %s unref (kicking and closing)", t->serial);
t->connection()->Stop();
- remove_transport(t);
+ if (t->IsTcpDevice() && !t->kicked()) {
+ D("transport: %s unref (attempting reconnection) %d", t->serial, t->kicked());
+ reconnect_handler.TrackTransport(t);
+ } else {
+ D("transport: %s unref (kicking and closing)", t->serial);
+ remove_transport(t);
+ }
} else {
D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
}
@@ -708,22 +847,41 @@
}
lock.unlock();
- // Don't return unauthorized devices; the caller can't do anything with them.
- if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
- *error_out = "device unauthorized.\n";
- char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
- *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
- *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
- *error_out += "\n";
- *error_out += "Try 'adb kill-server' if that seems wrong.\n";
- *error_out += "Otherwise check for a confirmation dialog on your device.";
- result = nullptr;
- }
+ if (result && !accept_any_state) {
+ // The caller requires an active transport.
+ // Make sure that we're actually connected.
+ ConnectionState state = result->GetConnectionState();
+ switch (state) {
+ case kCsConnecting:
+ *error_out = "device still connecting";
+ result = nullptr;
+ break;
- // Don't return offline devices; the caller can't do anything with them.
- if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
- *error_out = "device offline";
- result = nullptr;
+ case kCsAuthorizing:
+ *error_out = "device still authorizing";
+ result = nullptr;
+ break;
+
+ case kCsUnauthorized: {
+ *error_out = "device unauthorized.\n";
+ char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+ *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+ *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+ *error_out += "\n";
+ *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+ *error_out += "Otherwise check for a confirmation dialog on your device.";
+ result = nullptr;
+ break;
+ }
+
+ case kCsOffline:
+ *error_out = "device offline";
+ result = nullptr;
+ break;
+
+ default:
+ break;
+ }
}
if (result) {
@@ -762,9 +920,8 @@
}
void atransport::Kick() {
- if (!kicked_) {
- D("kicking transport %s", this->serial);
- kicked_ = true;
+ if (!kicked_.exchange(true)) {
+ D("kicking transport %p %s", this, this->serial);
this->connection()->Stop();
}
}
@@ -802,6 +959,10 @@
return "sideload";
case kCsUnauthorized:
return "unauthorized";
+ case kCsAuthorizing:
+ return "authorizing";
+ case kCsConnecting:
+ return "connecting";
default:
return "unknown";
}
@@ -918,6 +1079,10 @@
connection_waitable_->SetConnectionEstablished(success);
}
+bool atransport::Reconnect() {
+ return reconnect_(this);
+}
+
#if ADB_HOST
// We use newline as our delimiter, make sure to never output it.
@@ -998,8 +1163,9 @@
}
#endif // ADB_HOST
-int register_socket_transport(int s, const char* serial, int port, int local) {
- atransport* t = new atransport();
+int register_socket_transport(int s, const char* serial, int port, int local,
+ atransport::ReconnectCallback reconnect) {
+ atransport* t = new atransport(std::move(reconnect), kCsOffline);
if (!serial) {
char buf[32];
@@ -1080,7 +1246,7 @@
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
- atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
+ atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm);
D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
init_usb_transport(t, usb);
diff --git a/adb/transport.h b/adb/transport.h
index ebc186b..ae9cc02 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -198,20 +198,27 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsOffline)
+ using ReconnectCallback = std::function<bool(atransport*)>;
+
+ atransport(ReconnectCallback reconnect, ConnectionState state)
: id(NextTransportId()),
+ kicked_(false),
connection_state_(state),
connection_waitable_(std::make_shared<ConnectionWaitable>()),
- connection_(nullptr) {
+ connection_(nullptr),
+ reconnect_(std::move(reconnect)) {
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
max_payload = MAX_PAYLOAD;
}
+ atransport(ConnectionState state = kCsOffline)
+ : atransport([](atransport*) { return false; }, state) {}
virtual ~atransport();
int Write(apacket* p);
void Kick();
+ bool kicked() const { return kicked_; }
// ConnectionState can be read by all threads, but can only be written in the main thread.
ConnectionState GetConnectionState() const;
@@ -286,8 +293,12 @@
// Gets a shared reference to the ConnectionWaitable.
std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
+ // Attempts to reconnect with the underlying Connection. Returns true if the
+ // reconnection attempt succeeded.
+ bool Reconnect();
+
private:
- bool kicked_ = false;
+ std::atomic<bool> kicked_;
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -310,6 +321,9 @@
// The underlying connection object.
std::shared_ptr<Connection> connection_ GUARDED_BY(mutex_);
+ // A callback that will be invoked when the atransport needs to reconnect.
+ ReconnectCallback reconnect_;
+
std::mutex mutex_;
DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -333,6 +347,7 @@
// Stops iteration and returns false if fn returns false, otherwise returns true.
bool iterate_transports(std::function<bool(const atransport*)> fn);
+void init_reconnect_handler(void);
void init_transport_registration(void);
void init_mdns_transport_discovery(void);
std::string list_transports(bool long_listing);
@@ -347,7 +362,8 @@
void connect_device(const std::string& address, std::string* response);
/* cause new transports to be init'd and added to the list */
-int register_socket_transport(int s, const char* serial, int port, int local);
+int register_socket_transport(int s, const char* serial, int port, int local,
+ atransport::ReconnectCallback reconnect);
// This should only be used for transports with connection_state == kCsNoPerm.
void unregister_usb_transport(usb_handle* usb);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index e81f27c..181d666 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -68,28 +68,24 @@
return local_connect_arbitrary_ports(port - 1, port, &dummy) == 0;
}
-void connect_device(const std::string& address, std::string* response) {
- if (address.empty()) {
- *response = "empty address";
- return;
- }
-
+std::tuple<unique_fd, int, std::string> tcp_connect(const std::string& address,
+ std::string* response) {
std::string serial;
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
- return;
+ return std::make_tuple(unique_fd(), port, serial);
}
std::string error;
- int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &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());
- return;
+ return std::make_tuple(std::move(fd), port, serial);
}
- D("client: connected %s remote on fd %d", serial.c_str(), fd);
+ D("client: connected %s remote on fd %d", serial.c_str(), fd.get());
close_on_exec(fd);
disable_tcp_nagle(fd);
@@ -98,7 +94,38 @@
D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
}
- int ret = register_socket_transport(fd, serial.c_str(), port, 0);
+ return std::make_tuple(std::move(fd), port, serial);
+}
+
+void connect_device(const std::string& address, std::string* response) {
+ if (address.empty()) {
+ *response = "empty address";
+ return;
+ }
+
+ unique_fd fd;
+ int port;
+ std::string serial;
+ std::tie(fd, port, serial) = tcp_connect(address, response);
+ auto reconnect = [address](atransport* t) {
+ std::string response;
+ unique_fd fd;
+ int port;
+ std::string serial;
+ std::tie(fd, port, serial) = tcp_connect(address, &response);
+ if (fd == -1) {
+ D("reconnect failed: %s", response.c_str());
+ return false;
+ }
+
+ // 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*
+ // that will in turn send the CNXN packet.
+ return init_socket_transport(t, fd.release(), port, 0) >= 0;
+ };
+
+ int ret = register_socket_transport(fd.release(), serial.c_str(), port, 0, std::move(reconnect));
if (ret < 0) {
adb_close(fd);
if (ret == -EALREADY) {
@@ -135,7 +162,8 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
std::string serial = getEmulatorSerialString(console_port);
- if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+ if (register_socket_transport(fd, serial.c_str(), adb_port, 1,
+ [](atransport*) { return false; }) == 0) {
return 0;
}
adb_close(fd);
@@ -239,7 +267,8 @@
close_on_exec(fd);
disable_tcp_nagle(fd);
std::string serial = android::base::StringPrintf("host-%d", fd);
- if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
+ if (register_socket_transport(fd, serial.c_str(), port, 1,
+ [](atransport*) { return false; }) != 0) {
adb_close(fd);
}
}
@@ -338,7 +367,8 @@
/* Host is connected. Register the transport, and start the
* exchange. */
std::string serial = android::base::StringPrintf("host-%d", fd);
- if (register_socket_transport(fd, serial.c_str(), port, 1) != 0 ||
+ if (register_socket_transport(fd, serial.c_str(), port, 1,
+ [](atransport*) { return false; }) != 0 ||
!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
adb_close(fd);
}
diff --git a/adb/types.h b/adb/types.h
index dd3e063..c6b3f07 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -17,11 +17,15 @@
#pragma once
#include <algorithm>
+#include <deque>
+#include <type_traits>
#include <utility>
+#include <vector>
#include <android-base/logging.h>
#include "sysdeps/memory.h"
+#include "sysdeps/uio.h"
// Essentially std::vector<char>, except without zero initialization or reallocation.
struct Block {
@@ -130,34 +134,205 @@
payload_type payload;
};
-struct Range {
- explicit Range(apacket::payload_type data) : data_(std::move(data)) {}
+struct IOVector {
+ using value_type = char;
+ using block_type = Block;
+ using size_type = size_t;
- Range(const Range& copy) = delete;
- Range& operator=(const Range& copy) = delete;
+ IOVector() {}
- Range(Range&& move) = default;
- Range& operator=(Range&& move) = default;
+ explicit IOVector(std::unique_ptr<block_type> block) {
+ append(std::move(block));
+ }
- size_t size() const { return data_.size() - begin_offset_ - end_offset_; };
+ IOVector(const IOVector& copy) = delete;
+ IOVector(IOVector&& move) : IOVector() {
+ *this = std::move(move);
+ }
+
+ IOVector& operator=(const IOVector& copy) = delete;
+ IOVector& operator=(IOVector&& move) {
+ chain_ = std::move(move.chain_);
+ chain_length_ = move.chain_length_;
+ begin_offset_ = move.begin_offset_;
+ end_offset_ = move.end_offset_;
+
+ move.chain_.clear();
+ move.chain_length_ = 0;
+ move.begin_offset_ = 0;
+ move.end_offset_ = 0;
+
+ return *this;
+ }
+
+ size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
bool empty() const { return size() == 0; }
- void drop_front(size_t n) {
- CHECK_GE(size(), n);
- begin_offset_ += n;
+ void clear() {
+ chain_length_ = 0;
+ begin_offset_ = 0;
+ end_offset_ = 0;
+ chain_.clear();
}
- void drop_end(size_t n) {
- CHECK_GE(size(), n);
- end_offset_ += n;
+ // Split the first |len| bytes out of this chain into its own.
+ IOVector take_front(size_type len) {
+ IOVector head;
+
+ if (len == 0) {
+ return head;
+ }
+ CHECK_GE(size(), len);
+
+ std::shared_ptr<const block_type> first_block = chain_.front();
+ CHECK_GE(first_block->size(), begin_offset_);
+ head.append_shared(std::move(first_block));
+ head.begin_offset_ = begin_offset_;
+
+ while (head.size() < len) {
+ pop_front_block();
+ CHECK(!chain_.empty());
+
+ head.append_shared(chain_.front());
+ }
+
+ if (head.size() == len) {
+ // Head takes full ownership of the last block it took.
+ head.end_offset_ = 0;
+ begin_offset_ = 0;
+ pop_front_block();
+ } else {
+ // Head takes partial ownership of the last block it took.
+ size_t bytes_taken = head.size() - len;
+ head.end_offset_ = bytes_taken;
+ CHECK_GE(chain_.front()->size(), bytes_taken);
+ begin_offset_ = chain_.front()->size() - bytes_taken;
+ }
+
+ return head;
}
- char* data() { return &data_[0] + begin_offset_; }
+ // 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());
+ CHECK_EQ(0ULL, end_offset_);
+ chain_length_ += block->size();
+ chain_.emplace_back(std::move(block));
+ }
- apacket::payload_type::iterator begin() { return data_.begin() + begin_offset_; }
- apacket::payload_type::iterator end() { return data_.end() - end_offset_; }
+ void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
- apacket::payload_type data_;
+ void trim_front() {
+ if (begin_offset_ == 0) {
+ return;
+ }
+
+ const block_type* first_block = chain_.front().get();
+ auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
+ memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
+ chain_.front() = std::move(copy);
+
+ chain_length_ -= begin_offset_;
+ begin_offset_ = 0;
+ }
+
+ private:
+ // append, except takes a shared_ptr.
+ // Private to prevent exterior mutation of blocks.
+ void append_shared(std::shared_ptr<const block_type> block) {
+ CHECK_NE(0ULL, block->size());
+ CHECK_EQ(0ULL, end_offset_);
+ chain_length_ += block->size();
+ chain_.emplace_back(std::move(block));
+ }
+
+ // Drop the front block from the chain, and update chain_length_ appropriately.
+ void pop_front_block() {
+ chain_length_ -= chain_.front()->size();
+ begin_offset_ = 0;
+ chain_.pop_front();
+ }
+
+ // Iterate over the blocks with a callback with an operator()(const char*, size_t).
+ template <typename Fn>
+ void iterate_blocks(Fn&& callback) const {
+ if (chain_.size() == 0) {
+ return;
+ }
+
+ for (size_t i = 0; i < chain_.size(); ++i) {
+ const std::shared_ptr<const block_type>& block = chain_.at(i);
+ const char* begin = block->data();
+ size_t length = block->size();
+
+ // Note that both of these conditions can be true if there's only one block.
+ if (i == 0) {
+ CHECK_GE(block->size(), begin_offset_);
+ begin += begin_offset_;
+ length -= begin_offset_;
+ }
+
+ if (i == chain_.size() - 1) {
+ CHECK_GE(length, end_offset_);
+ length -= end_offset_;
+ }
+
+ callback(begin, length);
+ }
+ }
+
+ public:
+ // Copy all of the blocks into a single block.
+ template <typename CollectionType = block_type>
+ CollectionType coalesce() const {
+ CollectionType result;
+ if (size() == 0) {
+ return result;
+ }
+
+ result.resize(size());
+
+ size_t offset = 0;
+ iterate_blocks([&offset, &result](const char* data, size_t len) {
+ memcpy(&result[offset], data, len);
+ offset += len;
+ });
+
+ return result;
+ }
+
+ template <typename FunctionType>
+ auto coalesced(FunctionType&& f) const ->
+ typename std::result_of<FunctionType(const char*, size_t)>::type {
+ if (chain_.size() == 1) {
+ // If we only have one block, we can use it directly.
+ return f(chain_.front()->data() + begin_offset_, size());
+ } else {
+ // Otherwise, copy to a single block.
+ auto data = coalesce();
+ return f(data.data(), data.size());
+ }
+ }
+
+ // Get a list of iovecs that can be used to write out all of the blocks.
+ std::vector<adb_iovec> iovecs() const {
+ std::vector<adb_iovec> result;
+ iterate_blocks([&result](const char* data, size_t len) {
+ adb_iovec iov;
+ iov.iov_base = const_cast<char*>(data);
+ iov.iov_len = len;
+ result.emplace_back(iov);
+ });
+
+ return result;
+ }
+
+ private:
+ // Total length of all of the blocks in the chain.
+ size_t chain_length_ = 0;
+
size_t begin_offset_ = 0;
size_t end_offset_ = 0;
+ std::deque<std::shared_ptr<const block_type>> chain_;
};
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
new file mode 100644
index 0000000..31ab90a
--- /dev/null
+++ b/adb/types_test.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "sysdeps/memory.h"
+#include "types.h"
+
+static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
+ return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+}
+
+static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
+ auto block = std::make_unique<IOVector::block_type>();
+ block->resize(len);
+ memset(&(*block)[0], value, len);
+ return block;
+}
+
+template <typename T>
+static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
+ auto copy = std::make_unique<IOVector::block_type>();
+ copy->assign(block->begin(), block->end());
+ return copy;
+}
+
+TEST(IOVector, empty) {
+ // Empty IOVector.
+ IOVector bc;
+ CHECK_EQ(0ULL, bc.coalesce().size());
+}
+
+TEST(IOVector, single_block) {
+ // A single block.
+ auto block = create_block('x', 100);
+ IOVector bc;
+ bc.append(copy_block(block));
+ ASSERT_EQ(100ULL, bc.size());
+ auto coalesced = bc.coalesce();
+ ASSERT_EQ(*block, coalesced);
+}
+
+TEST(IOVector, single_block_split) {
+ // One block split.
+ IOVector bc;
+ bc.append(create_block("foobar"));
+ IOVector foo = bc.take_front(3);
+ ASSERT_EQ(3ULL, foo.size());
+ ASSERT_EQ(3ULL, bc.size());
+ ASSERT_EQ(*create_block("foo"), foo.coalesce());
+ ASSERT_EQ(*create_block("bar"), bc.coalesce());
+}
+
+TEST(IOVector, aligned_split) {
+ IOVector bc;
+ bc.append(create_block("foo"));
+ bc.append(create_block("bar"));
+ bc.append(create_block("baz"));
+ ASSERT_EQ(9ULL, bc.size());
+
+ IOVector foo = bc.take_front(3);
+ ASSERT_EQ(3ULL, foo.size());
+ ASSERT_EQ(*create_block("foo"), foo.coalesce());
+
+ IOVector bar = bc.take_front(3);
+ ASSERT_EQ(3ULL, bar.size());
+ ASSERT_EQ(*create_block("bar"), bar.coalesce());
+
+ IOVector baz = bc.take_front(3);
+ ASSERT_EQ(3ULL, baz.size());
+ ASSERT_EQ(*create_block("baz"), baz.coalesce());
+
+ ASSERT_EQ(0ULL, bc.size());
+}
+
+TEST(IOVector, misaligned_split) {
+ IOVector bc;
+ bc.append(create_block("foo"));
+ bc.append(create_block("bar"));
+ bc.append(create_block("baz"));
+ bc.append(create_block("qux"));
+ bc.append(create_block("quux"));
+
+ // Aligned left, misaligned right, across multiple blocks.
+ IOVector foob = bc.take_front(4);
+ ASSERT_EQ(4ULL, foob.size());
+ ASSERT_EQ(*create_block("foob"), foob.coalesce());
+
+ // Misaligned left, misaligned right, in one block.
+ IOVector a = bc.take_front(1);
+ ASSERT_EQ(1ULL, a.size());
+ ASSERT_EQ(*create_block("a"), a.coalesce());
+
+ // Misaligned left, misaligned right, across two blocks.
+ IOVector rba = bc.take_front(3);
+ ASSERT_EQ(3ULL, rba.size());
+ ASSERT_EQ(*create_block("rba"), rba.coalesce());
+
+ // Misaligned left, misaligned right, across three blocks.
+ IOVector zquxquu = bc.take_front(7);
+ ASSERT_EQ(7ULL, zquxquu.size());
+ ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+
+ ASSERT_EQ(1ULL, bc.size());
+ ASSERT_EQ(*create_block("x"), bc.coalesce());
+}
diff --git a/base/Android.bp b/base/Android.bp
index ec81f61..3d80d97 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -26,6 +26,7 @@
cc_library_headers {
name: "libbase_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
@@ -47,6 +48,7 @@
"file.cpp",
"logging.cpp",
"parsenetaddress.cpp",
+ "properties.cpp",
"quick_exit.cpp",
"stringprintf.cpp",
"strings.cpp",
@@ -57,9 +59,6 @@
shared_libs: ["liblog"],
target: {
android: {
- srcs: [
- "properties.cpp",
- ],
sanitize: {
misc_undefined: ["integer"],
},
@@ -94,6 +93,7 @@
name: "libbase",
defaults: ["libbase_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
vndk: {
enabled: true,
@@ -128,6 +128,7 @@
"parsedouble_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
+ "properties_test.cpp",
"quick_exit_test.cpp",
"scopeguard_test.cpp",
"stringprintf_test.cpp",
@@ -137,7 +138,6 @@
],
target: {
android: {
- srcs: ["properties_test.cpp"],
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cc7aaf6..05a12e7 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -100,8 +100,17 @@
unsigned int, const char*)>;
using AbortFunction = std::function<void(const char*)>;
+// Loggers for use with InitLogging/SetLogger.
+
+// Log to the kernel log (dmesg).
void KernelLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log to stderr in the full logcat format (with pid/tid/time/tag details).
void StderrLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
+// Log just the message to stdout/stderr (without pid/tid/time/tag details).
+// The choice of stdout versus stderr is based on the severity.
+// Errors are also prefixed by the program name (as with err(3)/error(3)).
+// Useful for replacing printf(3)/perror(3)/err(3)/error(3) in command-line tools.
+void StdioLogger(LogId, LogSeverity, const char*, const char*, unsigned int, const char*);
void DefaultAborter(const char* abort_message);
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 041586c..3a05143 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -19,10 +19,6 @@
#include <sys/cdefs.h>
-#if !defined(__BIONIC__)
-#error Only bionic supports system properties.
-#endif
-
#include <chrono>
#include <limits>
#include <string>
@@ -62,14 +58,18 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
+#endif
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
+#if defined(__BIONIC__)
bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
std::chrono::milliseconds::max());
+#endif
} // namespace base
} // namespace android
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index b95fa07..b29676f 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -58,21 +58,33 @@
DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
};
-class CapturedStderr {
+class CapturedStdFd {
public:
- CapturedStderr();
- ~CapturedStderr();
+ CapturedStdFd(int std_fd);
+ ~CapturedStdFd();
int fd() const;
+ std::string str();
private:
- void init();
- void reset();
+ void Init();
+ void Reset();
TemporaryFile temp_file_;
- int old_stderr_;
+ int std_fd_;
+ int old_fd_;
- DISALLOW_COPY_AND_ASSIGN(CapturedStderr);
+ DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
+};
+
+class CapturedStderr : public CapturedStdFd {
+ public:
+ CapturedStderr() : CapturedStdFd(STDERR_FILENO) {}
+};
+
+class CapturedStdout : public CapturedStdFd {
+ public:
+ CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
};
#define ASSERT_MATCH(str, pattern) \
diff --git a/base/logging.cpp b/base/logging.cpp
index a33da22..978d56d 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -212,6 +212,16 @@
timestamp, getpid(), GetThreadId(), file, line, message);
}
+void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
+ unsigned int /*line*/, const char* message) {
+ if (severity >= WARNING) {
+ fflush(stdout);
+ fprintf(stderr, "%s: %s\n", getprogname(), message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
void DefaultAborter(const char* abort_message) {
#ifdef __ANDROID__
android_set_abort_message(abort_message);
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 5f689fa..75b4ea0 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,11 +206,9 @@
}
#endif
-static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity,
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
const char* expected, const char* expected_tag = nullptr) {
- std::string output;
- ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
- android::base::ReadFdToString(cap.fd(), &output);
+ std::string output = cap.str();
// We can't usefully check the output of any of these on Windows because we
// don't have std::regex, but we can at least make sure we printed at least as
@@ -623,3 +621,23 @@
}
CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
}
+
+TEST(logging, StdioLogger) {
+ std::string err_str;
+ std::string out_str;
+ {
+ CapturedStderr cap_err;
+ CapturedStdout cap_out;
+ android::base::SetLogger(android::base::StdioLogger);
+ LOG(INFO) << "out";
+ LOG(ERROR) << "err";
+ err_str = cap_err.str();
+ out_str = cap_out.str();
+ }
+
+ // For INFO we expect just the literal "out\n".
+ ASSERT_EQ("out\n", out_str) << out_str;
+ // Whereas ERROR logging includes the program name.
+ ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str)
+ << err_str;
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index 6cf43f9..d5a5918 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-
#include "android-base/properties.h"
+#if defined(__BIONIC__)
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/system_properties.h>
#include <sys/_system_properties.h>
+#endif
#include <algorithm>
#include <chrono>
#include <limits>
+#include <map>
#include <string>
#include <android-base/parseint.h>
@@ -31,24 +33,6 @@
namespace android {
namespace base {
-std::string GetProperty(const std::string& key, const std::string& default_value) {
- const prop_info* pi = __system_property_find(key.c_str());
- if (pi == nullptr) return default_value;
-
- std::string property_value;
- __system_property_read_callback(pi,
- [](void* cookie, const char*, const char* value, unsigned) {
- auto property_value = reinterpret_cast<std::string*>(cookie);
- *property_value = value;
- },
- &property_value);
-
- // If the property exists but is empty, also return the default value.
- // Since we can't remove system properties, "empty" is traditionally
- // the same as "missing" (this was true for cutils' property_get).
- return property_value.empty() ? default_value : property_value;
-}
-
bool GetBoolProperty(const std::string& key, bool default_value) {
std::string value = GetProperty(key, "");
if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
@@ -85,10 +69,43 @@
template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t);
template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t);
+#if !defined(__BIONIC__)
+static std::map<std::string, std::string>& g_properties = *new std::map<std::string, std::string>;
+static int __system_property_set(const char* key, const char* value) {
+ g_properties[key] = value;
+ return 0;
+}
+#endif
+
+std::string GetProperty(const std::string& key, const std::string& default_value) {
+ std::string property_value;
+#if defined(__BIONIC__)
+ const prop_info* pi = __system_property_find(key.c_str());
+ if (pi == nullptr) return default_value;
+
+ __system_property_read_callback(pi,
+ [](void* cookie, const char*, const char* value, unsigned) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ &property_value);
+#else
+ auto it = g_properties.find(key);
+ if (it == g_properties.end()) return default_value;
+ property_value = it->second;
+#endif
+ // If the property exists but is empty, also return the default value.
+ // Since we can't remove system properties, "empty" is traditionally
+ // the same as "missing" (this was true for cutils' property_get).
+ return property_value.empty() ? default_value : property_value;
+}
+
bool SetProperty(const std::string& key, const std::string& value) {
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
+#if defined(__BIONIC__)
+
struct WaitForPropertyData {
bool done;
const std::string* expected_value;
@@ -175,5 +192,7 @@
return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
+#endif
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index de5f3dc..e7d4880 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -23,7 +23,9 @@
#include <string>
#include <thread>
-using namespace std::chrono_literals;
+#if !defined(_WIN32)
+using namespace std::literals;
+#endif
TEST(properties, smoke) {
android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -126,6 +128,7 @@
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
TEST(properties, WaitForProperty) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
@@ -138,9 +141,13 @@
flag = true;
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
200ms));
@@ -149,9 +156,13 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_MaxTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -165,9 +176,13 @@
// Test that this does not immediately return false due to overflow issues with the timeout.
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForProperty_NegativeTimeout) {
+#if defined(__BIONIC__)
std::atomic<bool> flag{false};
std::thread thread([&]() {
android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
@@ -181,9 +196,13 @@
// Assert that this immediately returns with a negative timeout
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation) {
+#if defined(__BIONIC__)
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
@@ -192,9 +211,13 @@
ASSERT_TRUE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_test", 1s));
thread.join();
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
TEST(properties, WaitForPropertyCreation_timeout) {
+#if defined(__BIONIC__)
auto t0 = std::chrono::steady_clock::now();
ASSERT_FALSE(android::base::WaitForPropertyCreation(
"debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
@@ -203,4 +226,7 @@
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing on the host.\n";
+#endif
}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 1619c21..5096369 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "android-base/logging.h"
#include "android-base/test_utils.h"
#include <fcntl.h>
@@ -33,6 +32,9 @@
#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
#ifdef _WIN32
int mkstemp(char* template_name) {
if (_mktemp(template_name) == nullptr) {
@@ -123,31 +125,38 @@
return (mkdtemp(path) != nullptr);
}
-CapturedStderr::CapturedStderr() : old_stderr_(-1) {
- init();
+CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
+ Init();
}
-CapturedStderr::~CapturedStderr() {
- reset();
+CapturedStdFd::~CapturedStdFd() {
+ Reset();
}
-int CapturedStderr::fd() const {
+int CapturedStdFd::fd() const {
return temp_file_.fd;
}
-void CapturedStderr::init() {
+std::string CapturedStdFd::str() {
+ std::string result;
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+ android::base::ReadFdToString(fd(), &result);
+ return result;
+}
+
+void CapturedStdFd::Init() {
#if defined(_WIN32)
// On Windows, stderr is often buffered, so make sure it is unbuffered so
// that we can immediately read back what was written to stderr.
- CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+ if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
#endif
- old_stderr_ = dup(STDERR_FILENO);
- CHECK_NE(-1, old_stderr_);
- CHECK_NE(-1, dup2(fd(), STDERR_FILENO));
+ old_fd_ = dup(std_fd_);
+ CHECK_NE(-1, old_fd_);
+ CHECK_NE(-1, dup2(fd(), std_fd_));
}
-void CapturedStderr::reset() {
- CHECK_NE(-1, dup2(old_stderr_, STDERR_FILENO));
- CHECK_EQ(0, close(old_stderr_));
+void CapturedStdFd::Reset() {
+ CHECK_NE(-1, dup2(old_fd_, std_fd_));
+ CHECK_EQ(0, close(old_fd_));
// Note: cannot restore prior setvbuf() setting.
}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index b194bbe..8ed92a6 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -312,6 +312,7 @@
init : processing action (ro.boot.bootreason=*) 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)'
(/system/bin/bootstat -r post_decrypt_time_elapsed)'
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
@@ -444,6 +445,10 @@
*hw_reset* ) var="hard,hw_reset" ;;
*usb* ) var="cold,charger" ;;
*rtc* ) var="cold,rtc" ;;
+ *2sec_reboot* ) var="cold,rtc,2sec" ;;
+ *wdt_by_pass_pwk* ) var="warm" ;;
+ wdt ) var="reboot" ;;
+ *tool_by_pass_pwk* ) var="reboot,tool" ;;
*bootloader* ) var="bootloader" ;;
* ) var="reboot" ;;
esac
@@ -582,9 +587,9 @@
duration_test
case ${TEST} in
bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
- *) reason=reboot,${TEST} ;;
+ *) reason=reboot,${TEST#optional_} ;;
esac
- adb reboot ${TEST}
+ adb reboot ${TEST#optional_}
wait_for_screen
bootloader_reason=`validate_property ro.boot.bootreason`
EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}
@@ -948,6 +953,20 @@
report_bootstat_logs reboot,adb
}
+[ "USAGE: test_rescueparty
+
+rescueparty test
+- adb reboot rescueparty
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- adb shell getprop ro.boot.bootreason
+- NB: should report reboot,rescueparty" ]
+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
+}
+
[ "USAGE: test_Its_Just_So_Hard_reboot
Its Just So Hard reboot test:
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1b13b21..529d0c9 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -201,7 +201,7 @@
{"cold", 56},
{"hard", 57},
{"warm", 58},
- // {"recovery", 59}, // Duplicate of enum 3 above. Immediate reuse possible.
+ {"reboot,kernel_power_off_charging__reboot_system", 59}, // Can not happen
{"thermal-shutdown", 60},
{"shutdown,thermal", 61},
{"shutdown,battery", 62},
@@ -228,7 +228,7 @@
{"2sec_reboot", 83},
{"reboot,by_key", 84},
{"reboot,longkey", 85},
- {"reboot,2sec", 86},
+ {"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
@@ -306,6 +306,10 @@
{"kernel_panic,sysrq,livelock,alarm", 161}, // llkd
{"kernel_panic,sysrq,livelock,driver", 162}, // llkd
{"kernel_panic,sysrq,livelock,zombie", 163}, // llkd
+ {"kernel_panic,modem", 164},
+ {"kernel_panic,adsp", 165},
+ {"kernel_panic,dsps", 166},
+ {"kernel_panic,wcnss", 167},
};
// Converts a string value representing the reason the system booted to an
@@ -702,6 +706,10 @@
{"Corrupt kernel stack", "stack"},
{"low stack detected", "stack"},
{"corrupted stack end", "stack"},
+ {"subsys-restart: Resetting the SoC - modem crashed.", "modem"},
+ {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"},
+ {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"},
+ {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"},
};
ret = "kernel_panic";
@@ -782,7 +790,10 @@
{"hard,hw_reset", "hw_reset"},
{"cold,charger", "usb"},
{"cold,rtc", "rtc"},
- {"reboot,2sec", "2sec_reboot"},
+ {"cold,rtc,2sec", "2sec_reboot"},
+ {"!warm", "wdt_by_pass_pwk"}, // change flavour of blunt
+ {"!reboot", "^wdt$"}, // change flavour of blunt
+ {"reboot,tool", "tool_by_pass_pwk"},
{"bootloader", ""},
};
@@ -842,6 +853,10 @@
ret = "reboot," + subReason; // legitimize unknown reasons
}
}
+ // Some bootloaders shutdown results record in last kernel message.
+ if (!strcmp(ret.c_str(), "reboot,kernel_power_off_charging__reboot_system")) {
+ ret = "shutdown";
+ }
}
// Check for kernel panics, allowed to override reboot command.
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 9c556c3..0b13662 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
cc_library_headers {
name: "libdebuggerd_common_headers",
export_include_dirs: ["common/include"],
+ recovery_available: true,
}
cc_library_shared {
@@ -67,6 +68,7 @@
cc_library_static {
name: "libdebuggerd_handler_core",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_handler.cpp"],
header_libs: [
@@ -88,6 +90,7 @@
cc_library_static {
name: "libdebuggerd_handler",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: ["handler/debuggerd_fallback_nop.cpp"],
whole_static_libs: [
@@ -112,7 +115,6 @@
"libbase",
"libdebuggerd",
"libbacktrace",
- "libunwind",
"libunwindstack",
"libdexfile",
"liblzma",
@@ -144,6 +146,7 @@
cc_library_static {
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
+ recovery_available: true,
srcs: [
"libdebuggerd/backtrace.cpp",
@@ -158,7 +161,6 @@
static_libs: [
"libbacktrace",
- "libunwind",
"libunwindstack",
"liblzma",
"libbase",
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 4b32b9d..f31337d 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -197,6 +197,7 @@
fprintf(stderr, " LOG-FATAL call libbase LOG(FATAL)\n");
fprintf(stderr, "\n");
fprintf(stderr, " SIGFPE cause a SIGFPE\n");
+ fprintf(stderr, " SIGILL cause a SIGILL\n");
fprintf(stderr, " SIGSEGV cause a SIGSEGV at address 0x0 (synonym: crash)\n");
fprintf(stderr, " SIGSEGV-non-null cause a SIGSEGV at a non-zero address\n");
fprintf(stderr, " SIGSEGV-unmapped mmap/munmap a region of memory and then attempt to access it\n");
@@ -268,6 +269,16 @@
} else if (!strcasecmp(arg, "SIGFPE")) {
raise(SIGFPE);
return EXIT_SUCCESS;
+ } else if (!strcasecmp(arg, "SIGILL")) {
+#if defined(__aarch64__)
+ __asm__ volatile(".word 0\n");
+#elif defined(__arm__)
+ __asm__ volatile(".word 0xe7f0def0\n");
+#elif defined(__i386__) || defined(__x86_64__)
+ __asm__ volatile("ud2\n");
+#else
+#error
+#endif
} else if (!strcasecmp(arg, "SIGTRAP")) {
raise(SIGTRAP);
return EXIT_SUCCESS;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 9b64be7..dfb7a6a 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -80,8 +80,13 @@
return value; \
}()
+// Backtrace frame dump could contain:
+// #01 pc 0001cded /data/tmp/debuggerd_test32 (raise_debugger_signal+80)
+// or
+// #01 pc 00022a09 /data/tmp/debuggerd_test32 (offset 0x12000) (raise_debugger_signal+80)
#define ASSERT_BACKTRACE_FRAME(result, frame_name) \
- ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ \S+ \()" frame_name R"(\+)");
+ ASSERT_MATCH(result, \
+ R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
InterceptStatus* status, DebuggerdDumpType intercept_type) {
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index dea2e17..079a574 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -304,7 +304,16 @@
crash_mutex.lock();
if (lock_count++ > 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, aborting");
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGABRT);
+ sigprocmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // Just in case...
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "abort didn't exit, exiting");
_exit(1);
}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e11be1e..433bb46 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -102,10 +102,17 @@
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) {
- char addr_desc[32]; // ", fault addr 0x1234"
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
+ char addr_desc[64]; // ", fault addr 0x1234"
if (signal_has_si_addr(thread_info.siginfo)) {
- snprintf(addr_desc, sizeof(addr_desc), "%p", thread_info.siginfo->si_addr);
+ void* addr = thread_info.siginfo->si_addr;
+ if (thread_info.siginfo->si_signo == SIGILL) {
+ uint32_t instruction = {};
+ process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
+ snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
+ } else {
+ snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
+ }
} else {
snprintf(addr_desc, sizeof(addr_desc), "--------");
}
@@ -418,7 +425,7 @@
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
- dump_signal_info(log, thread_info);
+ dump_signal_info(log, thread_info, process_memory);
}
if (primary_thread) {
diff --git a/demangle/Android.bp b/demangle/Android.bp
index cf6abfd..fd79cf8 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -36,6 +36,7 @@
name: "libdemangle",
defaults: ["libdemangle_defaults"],
vendor_available: true,
+ recovery_available: true,
srcs: [
"Demangler.cpp",
diff --git a/demangle/Android.mk b/demangle/Android.mk
index e3cfc2a..d8082a9 100644
--- a/demangle/Android.mk
+++ b/demangle/Android.mk
@@ -19,7 +19,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE := demangle_fuzzer
-LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
Demangler.cpp \
demangle_fuzzer.cpp \
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index a7ecf37..6bee28c 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -2,6 +2,7 @@
name: "libdiagnose_usb",
cflags: ["-Wall", "-Wextra", "-Werror"],
host_supported: true,
+ recovery_available: true,
target: {
windows: {
enabled: true,
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 9bd0628..983e195 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -101,14 +101,13 @@
#
my_dist_files := $(HOST_OUT_EXECUTABLES)/fastboot
-my_dist_files += $(HOST_OUT_EXECUTABLES)/mke2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs$(HOST_EXECUTABLE_SUFFIX)
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs$(HOST_EXECUTABLE_SUFFIX)
+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
-# Archive fastboot.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
endif
my_dist_files :=
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 9463cc9..5aa87d9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -316,7 +316,8 @@
static int show_help() {
// clang-format off
fprintf(stdout,
-/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
+// 1 2 3 4 5 6 7 8
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
"usage: fastboot [OPTION...] COMMAND...\n"
"\n"
"flashing:\n"
@@ -324,8 +325,8 @@
" flashall Flash all partitions from $ANDROID_PRODUCT_OUT.\n"
" On A/B devices, flashed slot is set as active.\n"
" Secondary images may be flashed to inactive slot.\n"
- " flash PARTITION [FILENAME]\n"
- " Flash given partition only.\n"
+ " flash PARTITION [FILENAME] Flash given partition, using the image from\n"
+ " $ANDROID_PRODUCT_OUT if no filename is given.\n"
"\n"
"basics:\n"
" devices [-l] List devices in bootloader (-l: with device paths).\n"
@@ -507,7 +508,8 @@
static std::string make_temporary_directory() {
std::string result(make_temporary_template());
if (mkdtemp(&result[0]) == nullptr) {
- die("unable to create temporary directory: %s", strerror(errno));
+ die("unable to create temporary directory with template %s: %s",
+ result.c_str(), strerror(errno));
}
return result;
}
@@ -516,7 +518,8 @@
std::string path_template(make_temporary_template());
int fd = mkstemp(&path_template[0]);
if (fd == -1) {
- die("failed to create temporary file for %s: %s\n", what, strerror(errno));
+ die("failed to create temporary file for %s with template %s: %s\n",
+ path_template.c_str(), what, strerror(errno));
}
unlink(path_template.c_str());
return fd;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index f23150d..bc3b04b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -33,6 +33,7 @@
cc_library_static {
name: "libfs_mgr",
defaults: ["fs_mgr_defaults"],
+ recovery_available: true,
export_include_dirs: ["include"],
include_dirs: ["system/vold"],
srcs: [
@@ -42,6 +43,7 @@
"fs_mgr_verity.cpp",
"fs_mgr_avb.cpp",
"fs_mgr_avb_ops.cpp",
+ "fs_mgr_dm_linear.cpp",
],
static_libs: [
"libfec",
@@ -78,6 +80,7 @@
cc_library_static {
name: "libfstab",
vendor_available: true,
+ recovery_available: true,
defaults: ["fs_mgr_defaults"],
srcs: [
"fs_mgr_fstab.cpp",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6769fda..6e9ffba 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -794,6 +794,29 @@
return true;
}
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
+ // 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] == '/') {
+ return true;
+ }
+
+ android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDONLY));
+ if (dm_fd < 0) {
+ PLOG(ERROR) << "open /dev/device-mapper failed";
+ return false;
+ }
+ struct dm_ioctl io;
+ std::string device_name;
+ if (!fs_mgr_dm_get_device_name(&io, rec->blk_device, dm_fd, &device_name)) {
+ return false;
+ }
+ free(rec->blk_device);
+ rec->blk_device = strdup(device_name.c_str());
+ return true;
+}
+
/* 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.
@@ -845,6 +868,13 @@
}
}
+ if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+ if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+ LERROR << "Could not set up logical partition, skipping!";
+ 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";
@@ -1065,6 +1095,13 @@
return FS_MGR_DOMNT_FAILED;
}
+ if ((fstab->recs[i].fs_mgr_flags & MF_LOGICAL)) {
+ if (!fs_mgr_update_logical_partition(&fstab->recs[i])) {
+ LERROR << "Could not set up logical 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)) {
LERROR << "Skipping mounting '" << n_blk_device << "'";
@@ -1383,7 +1420,7 @@
mount_point = basename(fstab->recs[i].mount_point);
}
- fs_mgr_verity_ioctl_init(io, mount_point);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, mount_point);
const char* status;
if (ioctl(fd, DM_TABLE_STATUS, io)) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 6ea3833..5a9cb65 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -303,7 +303,7 @@
static bool load_verity_table(struct dm_ioctl* io, const std::string& dm_device_name, int fd,
uint64_t image_size, const std::string& verity_table) {
- fs_mgr_verity_ioctl_init(io, dm_device_name);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, dm_device_name);
// The buffer consists of [dm_ioctl][dm_target_spec][verity_params].
char* buffer = (char*)io;
@@ -360,14 +360,14 @@
alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl* io = (struct dm_ioctl*)buffer;
const std::string mount_point(basename(fstab_entry->mount_point));
- if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+ if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
LERROR << "Couldn't create verity device!";
return false;
}
// Gets the name of the device file.
std::string verity_blk_name;
- if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+ if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
LERROR << "Couldn't get verity device number!";
return false;
}
@@ -386,7 +386,7 @@
}
// Activates the device.
- if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
return false;
}
@@ -585,7 +585,13 @@
// 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(basename(fstab_entry->blk_device));
+ 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) {
diff --git a/fs_mgr/fs_mgr_dm_ioctl.cpp b/fs_mgr/fs_mgr_dm_ioctl.cpp
index 5f9b118..3a7fae4 100644
--- a/fs_mgr/fs_mgr_dm_ioctl.cpp
+++ b/fs_mgr/fs_mgr_dm_ioctl.cpp
@@ -23,9 +23,9 @@
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_dm_ioctl.h"
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name) {
- memset(io, 0, DM_BUF_SIZE);
- io->data_size = DM_BUF_SIZE;
+void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name) {
+ memset(io, 0, size);
+ io->data_size = size;
io->data_start = sizeof(struct dm_ioctl);
io->version[0] = 4;
io->version[1] = 0;
@@ -35,8 +35,8 @@
}
}
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name);
+bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_CREATE, io)) {
PERROR << "Error creating device mapping";
return false;
@@ -44,8 +44,8 @@
return true;
}
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name);
+bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_REMOVE, io)) {
PERROR << "Error removing device mapping";
return false;
@@ -53,13 +53,13 @@
return true;
}
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
- std::string* out_dev_name) {
+bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+ std::string* out_dev_name) {
FS_MGR_CHECK(out_dev_name != nullptr);
- fs_mgr_verity_ioctl_init(io, name);
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_STATUS, io)) {
- PERROR << "Error fetching verity device number";
+ PERROR << "Error fetching device-mapper device number";
return false;
}
@@ -69,10 +69,10 @@
return true;
}
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd) {
- fs_mgr_verity_ioctl_init(io, name);
+bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd) {
+ fs_mgr_dm_ioctl_init(io, sizeof(*io), name);
if (ioctl(fd, DM_DEV_SUSPEND, io)) {
- PERROR << "Error activating verity device";
+ PERROR << "Error activating device table";
return false;
}
return true;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
new file mode 100644
index 0000000..b2f3a68
--- /dev/null
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source 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.
+ */
+
+#include "fs_mgr_dm_linear.h"
+
+#include <inttypes.h>
+#include <linux/dm-ioctl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fs_mgr_priv.h"
+#include "fs_mgr_priv_dm_ioctl.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string LogicalPartitionExtent::Serialize() const {
+ // Note: we need to include an explicit null-terminator.
+ std::string argv =
+ android::base::StringPrintf("%s %" PRIu64, block_device_.c_str(), first_sector_);
+ argv.push_back(0);
+
+ // The kernel expects each target to be aligned.
+ size_t spec_bytes = sizeof(struct dm_target_spec) + argv.size();
+ size_t padding = ((spec_bytes + 7) & ~7) - spec_bytes;
+ for (size_t i = 0; i < padding; i++) {
+ argv.push_back(0);
+ }
+
+ struct dm_target_spec spec;
+ spec.sector_start = logical_sector_;
+ spec.length = num_sectors_;
+ spec.status = 0;
+ strcpy(spec.target_type, "linear");
+ spec.next = sizeof(struct dm_target_spec) + argv.size();
+
+ return std::string((char*)&spec, sizeof(spec)) + argv;
+}
+
+static bool LoadDmTable(int dm_fd, const LogicalPartition& partition) {
+ // Combine all dm_target_spec buffers together.
+ std::string target_string;
+ for (const auto& extent : partition.extents) {
+ target_string += extent.Serialize();
+ }
+
+ // Allocate the ioctl buffer.
+ size_t buffer_size = sizeof(struct dm_ioctl) + target_string.size();
+ std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(buffer_size);
+
+ // Initialize the ioctl buffer header, then copy our target specs in.
+ struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
+ fs_mgr_dm_ioctl_init(io, buffer_size, partition.name);
+ io->target_count = partition.extents.size();
+ if (partition.attributes & kPartitionReadonly) {
+ io->flags |= DM_READONLY_FLAG;
+ }
+ memcpy(io + 1, target_string.c_str(), target_string.size());
+
+ if (ioctl(dm_fd, DM_TABLE_LOAD, io)) {
+ PERROR << "Failed ioctl() on DM_TABLE_LOAD, partition " << partition.name;
+ return false;
+ }
+ return true;
+}
+
+static bool LoadTablesAndActivate(int dm_fd, const LogicalPartition& partition) {
+ if (!LoadDmTable(dm_fd, partition)) {
+ return false;
+ }
+
+ struct dm_ioctl io;
+ return fs_mgr_dm_resume_table(&io, partition.name, dm_fd);
+}
+
+static bool CreateDmDeviceForPartition(int dm_fd, const LogicalPartition& partition) {
+ struct dm_ioctl io;
+ if (!fs_mgr_dm_create_device(&io, partition.name, dm_fd)) {
+ return false;
+ }
+ if (!LoadTablesAndActivate(dm_fd, partition)) {
+ // Remove the device rather than leave it in an inactive state.
+ fs_mgr_dm_destroy_device(&io, partition.name, dm_fd);
+ return false;
+ }
+
+ LINFO << "Created device-mapper device: " << partition.name;
+ return true;
+}
+
+bool CreateLogicalPartitions(const LogicalPartitionTable& table) {
+ android::base::unique_fd dm_fd(open("/dev/device-mapper", O_RDWR));
+ if (dm_fd < 0) {
+ PLOG(ERROR) << "failed to open /dev/device-mapper";
+ return false;
+ }
+ for (const auto& partition : table.partitions) {
+ if (!CreateDmDeviceForPartition(dm_fd, partition)) {
+ LOG(ERROR) << "could not create dm-linear device for partition: " << partition.name;
+ return false;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree() {
+ return nullptr;
+}
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 3fbef9f..af4d6c1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -109,6 +109,7 @@
{"logicalblk=", MF_LOGICALBLKSIZE},
{"sysfs_path=", MF_SYSFS},
{"defaults", 0},
+ {"logical", MF_LOGICAL},
{0, 0},
};
@@ -445,10 +446,6 @@
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
return {};
}
- if (!StartsWith(value, "/dev")) {
- LERROR << "dt_fstab: Invalid device node for partition " << dp->d_name;
- return {};
- }
fstab_entry.push_back(value);
std::string mount_point;
@@ -631,6 +628,10 @@
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);
+ }
+
cnt++;
}
/* If an A/B partition, modify block device to be the real block device */
@@ -685,6 +686,49 @@
return a;
}
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ * /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ * /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ * /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> boot_devices;
+
+ for (int i = 0; i < fstab.num_entries; i++) {
+ std::string blk_device(fstab.recs[i].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") ||
+ android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+ continue;
+ }
+ // Skips non-by_name blk_device.
+ // /dev/block/<type>/<device>/by-name/<partition>
+ // ^ slash_by_name
+ auto slash_by_name = blk_device.find("/by-name");
+ if (slash_by_name == std::string::npos) continue;
+ blk_device.erase(slash_by_name); // erases /by-name/<partition>
+
+ // Erases /dev/block/, now we have <type>/<device>
+ blk_device.erase(0, std::string("/dev/block/").size());
+
+ // <type>/<device>
+ // ^ first_slash
+ auto first_slash = blk_device.find('/');
+ if (first_slash == std::string::npos) continue;
+
+ auto boot_device = blk_device.substr(first_slash + 1);
+ if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+ }
+
+ return boot_devices;
+}
+
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
FILE *fstab_file;
@@ -797,6 +841,7 @@
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);
@@ -861,6 +906,23 @@
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, ",");
+ 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);
+
+ return {};
+}
+
int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
@@ -944,3 +1006,7 @@
{
return fstab->fs_mgr_flags & MF_SYSFS;
}
+
+int fs_mgr_is_logical(const struct fstab_rec* fstab) {
+ return fstab->fs_mgr_flags & MF_LOGICAL;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ade0cc4..afd7227 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -82,6 +82,7 @@
*
*/
+// clang-format off
#define MF_WAIT 0x1
#define MF_CHECK 0x2
#define MF_CRYPT 0x4
@@ -111,6 +112,8 @@
#define MF_AVB 0X2000000
#define MF_KEYDIRECTORY 0X4000000
#define MF_SYSFS 0X8000000
+#define MF_LOGICAL 0x10000000
+// clang-format on
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/fs_mgr_priv_dm_ioctl.h b/fs_mgr/fs_mgr_priv_dm_ioctl.h
index 792683e..792475d 100644
--- a/fs_mgr/fs_mgr_priv_dm_ioctl.h
+++ b/fs_mgr/fs_mgr_priv_dm_ioctl.h
@@ -20,15 +20,15 @@
#include <linux/dm-ioctl.h>
#include <string>
-void fs_mgr_verity_ioctl_init(struct dm_ioctl* io, const std::string& name);
+void fs_mgr_dm_ioctl_init(struct dm_ioctl* io, size_t size, const std::string& name);
-bool fs_mgr_create_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_create_device(struct dm_ioctl* io, const std::string& name, int fd);
-bool fs_mgr_destroy_verity_device(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_destroy_device(struct dm_ioctl* io, const std::string& name, int fd);
-bool fs_mgr_get_verity_device_name(struct dm_ioctl* io, const std::string& name, int fd,
- std::string* out_dev_name);
+bool fs_mgr_dm_get_device_name(struct dm_ioctl* io, const std::string& name, int fd,
+ std::string* out_dev_name);
-bool fs_mgr_resume_verity_table(struct dm_ioctl* io, const std::string& name, int fd);
+bool fs_mgr_dm_resume_table(struct dm_ioctl* io, const std::string& name, int fd);
#endif /* __CORE_FS_MGR_PRIV_DM_IOCTL_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 0a113b4..3b01d0e 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -36,19 +36,30 @@
std::string ab_suffix;
for (n = 0; n < fstab->num_entries; n++) {
- if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
- char *tmp;
+ 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;
}
- if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, ab_suffix.c_str()) > 0) {
- free(fstab->recs[n].blk_device);
- fstab->recs[n].blk_device = tmp;
- } else {
+
+ 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;
+ }
}
}
return true;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index d0bb058..fe41f8a 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -258,7 +258,7 @@
char *buffer = (char*) io;
size_t bufsize;
- fs_mgr_verity_ioctl_init(io, name);
+ fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, name);
struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
@@ -805,13 +805,13 @@
}
// create the device
- if (!fs_mgr_create_verity_device(io, mount_point, fd)) {
+ if (!fs_mgr_dm_create_device(io, mount_point, fd)) {
LERROR << "Couldn't create verity device!";
goto out;
}
// get the name of the device file
- if (!fs_mgr_get_verity_device_name(io, mount_point, fd, &verity_blk_name)) {
+ if (!fs_mgr_dm_get_device_name(io, mount_point, fd, &verity_blk_name)) {
LERROR << "Couldn't get verity device number!";
goto out;
}
@@ -900,7 +900,7 @@
loaded:
// activate the device
- if (!fs_mgr_resume_verity_table(io, mount_point, fd)) {
+ if (!fs_mgr_dm_resume_table(io, mount_point, fd)) {
goto out;
}
@@ -923,7 +923,7 @@
if (!verified_at_boot) {
free(fstab->blk_device);
fstab->blk_device = strdup(verity_blk_name.c_str());
- } else if (!fs_mgr_destroy_verity_device(io, mount_point, fd)) {
+ } else if (!fs_mgr_dm_destroy_device(io, mount_point, fd)) {
LERROR << "Failed to remove verity device " << mount_point.c_str();
goto out;
}
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 653d8fa..72f019e 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -76,6 +76,7 @@
bool fs_mgr_load_verity_state(int* mode);
bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback);
int fs_mgr_swapon_all(struct fstab *fstab);
+bool fs_mgr_update_logical_partition(struct fstab_rec* rec);
int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
new file mode 100644
index 0000000..7f928e5
--- /dev/null
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source 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.
+ */
+
+#ifndef __CORE_FS_MGR_DM_LINEAR_H
+#define __CORE_FS_MGR_DM_LINEAR_H
+
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace fs_mgr {
+
+static const uint32_t kPartitionReadonly = 0x1;
+
+class LogicalPartitionExtent {
+ public:
+ LogicalPartitionExtent() : logical_sector_(0), first_sector_(0), num_sectors_(0) {}
+ LogicalPartitionExtent(uint64_t logical_sector, uint64_t first_sector, uint64_t num_sectors,
+ const std::string& block_device)
+ : logical_sector_(logical_sector),
+ first_sector_(first_sector),
+ num_sectors_(num_sectors),
+ block_device_(block_device) {}
+
+ // Return a string containing the dm_target_spec buffer needed to use this
+ // extent in a device-mapper table.
+ std::string Serialize() const;
+
+ const std::string& block_device() const { return block_device_; }
+
+ private:
+ // Logical sector this extent represents in the presented block device.
+ // This is equal to the previous extent's logical sector plus the number
+ // of sectors in that extent. The first extent always starts at 0.
+ uint64_t logical_sector_;
+ // First 512-byte sector of this extent, on the source block device.
+ uint64_t first_sector_;
+ // Number of 512-byte sectors.
+ uint64_t num_sectors_;
+ // Target block device.
+ std::string block_device_;
+};
+
+struct LogicalPartition {
+ LogicalPartition() : attributes(0), num_sectors(0) {}
+
+ std::string name;
+ uint32_t attributes;
+ // Number of 512-byte sectors total.
+ uint64_t num_sectors;
+ // List of extents.
+ std::vector<LogicalPartitionExtent> extents;
+};
+
+struct LogicalPartitionTable {
+ // List of partitions in the partition table.
+ std::vector<LogicalPartition> partitions;
+};
+
+// Load a dm-linear table from the device tree if one is available; otherwise,
+// return null.
+std::unique_ptr<LogicalPartitionTable> LoadPartitionsFromDeviceTree();
+
+// Create device-mapper devices for the given partition table.
+//
+// On success, two devices nodes will be created for each partition, both
+// pointing to the same device:
+// /dev/block/dm-<N> where N is a sequential ID assigned by device-mapper.
+// /dev/block/dm-<name> where |name| is the partition name.
+//
+bool CreateLogicalPartitions(const LogicalPartitionTable& table);
+
+} // namespace fs_mgr
+} // namespace android
+
+#endif // __CORE_FS_MGR_DM_LINEAR_H
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 8c585dd..d232cca 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdio.h>
+#include <set>
#include <string>
/*
@@ -37,6 +38,7 @@
struct fstab_rec {
char* blk_device;
+ char* logical_partition_name;
char* mount_point;
char* fs_type;
unsigned long flags;
@@ -84,8 +86,10 @@
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_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
+std::set<std::string> fs_mgr_get_boot_devices();
#endif /* __CORE_FS_TAB_H */
diff --git a/init/Android.bp b/init/Android.bp
index a31c5a5..25877c0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -100,6 +100,7 @@
"capabilities.cpp",
"descriptors.cpp",
"devices.cpp",
+ "epoll.cpp",
"firmware_handler.cpp",
"import_parser.cpp",
"init.cpp",
@@ -169,6 +170,7 @@
srcs: [
"devices_test.cpp",
"init_test.cpp",
+ "keychords_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
"property_type_test.cpp",
@@ -231,8 +233,11 @@
"action_parser.cpp",
"capabilities.cpp",
"descriptors.cpp",
+ "epoll.cpp",
+ "keychords.cpp",
"import_parser.cpp",
- "host_init_parser.cpp",
+ "host_import_parser.cpp",
+ "host_init_verifier.cpp",
"host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
@@ -245,7 +250,10 @@
proto: {
type: "lite",
},
- generated_headers: ["generated_stub_builtin_function_map"],
+ generated_headers: [
+ "generated_stub_builtin_function_map",
+ "generated_android_ids"
+ ],
target: {
android: {
enabled: false,
diff --git a/init/README.md b/init/README.md
index c08b07a..550ef05 100644
--- a/init/README.md
+++ b/init/README.md
@@ -195,6 +195,10 @@
> This service will not automatically start with its class.
It must be explicitly started by name or by interface name.
+`enter_namespace <type> <path>`
+> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with
+ _type_ set to "net". Note that only one namespace of a given _type_ may be entered.
+
`file <path> <type>`
> Open a file path and pass its fd to the launched process. _type_ must be
"r", "w" or "rw". For native executables see libcutils
diff --git a/init/action.cpp b/init/action.cpp
index f782b51..11335ca 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,16 +18,11 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
using android::base::Join;
namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a2c9671..8a4b518 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,16 +16,11 @@
#include "action_parser.h"
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
-#if defined(__ANDROID__)
-#include <android-base/properties.h>
-#else
-#include "host_init_stubs.h"
-#endif
-
using android::base::GetBoolProperty;
using android::base::StartsWith;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8bd92cc..17d34e1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -240,6 +240,29 @@
return Success();
}
+static Result<Success> 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();
+}
+
+static Result<Success> 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();
+}
+
+static Result<Success> 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();
+}
+
// mkdir <path> [mode] [owner] [group]
static Result<Success> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
@@ -1050,6 +1073,9 @@
{"init_user0", {0, 0, {false, do_init_user0}}},
{"insmod", {1, kMax, {true, do_insmod}}},
{"installkey", {1, 1, {false, do_installkey}}},
+ {"interface_restart", {1, 1, {false, do_interface_restart}}},
+ {"interface_start", {1, 1, {false, do_interface_start}}},
+ {"interface_stop", {1, 1, {false, do_interface_stop}}},
{"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}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index 8d27f4f..ada1e28 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -329,10 +329,10 @@
<< partition_name_sanitized << "'";
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
- }
-
- if (uevent.partition_num >= 0) {
- links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num));
+ // Adds symlink: /dev/block/by-name/<partition_name>.
+ if (boot_devices_.find(device) != boot_devices_.end()) {
+ links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
+ }
}
auto last_slash = uevent.path.rfind('/');
@@ -350,8 +350,14 @@
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
- if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) {
- PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ if (symlink(devpath.c_str(), link.c_str())) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
+ } else if (std::string link_path;
+ Readlink(link, &link_path) && link_path != devpath) {
+ PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
+ << ", which already links to: " << link_path;
+ }
}
}
}
@@ -415,16 +421,18 @@
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon)
+ std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+ bool skip_restorecon)
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
+ boot_devices_(std::move(boot_devices)),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
- std::vector<Subsystem>{}, false) {}
+ std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
} // namespace init
} // namespace android
diff --git a/init/devices.h b/init/devices.h
index 1f8f1e8..f9035da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <algorithm>
+#include <set>
#include <string>
#include <vector>
@@ -103,8 +104,8 @@
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
- std::vector<SysfsPermissions> sysfs_permissions,
- std::vector<Subsystem> subsystems, bool skip_restorecon);
+ std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
+ std::set<std::string> boot_devices, bool skip_restorecon);
~DeviceHandler(){};
void HandleDeviceEvent(const Uevent& uevent);
@@ -125,6 +126,7 @@
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
+ std::set<std::string> boot_devices_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index eba00cb..d658f4d 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -84,7 +84,6 @@
};
std::vector<std::string> expected_result{
"/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
- "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
@@ -100,7 +99,6 @@
.partition_num = 1,
};
std::vector<std::string> expected_result{
- "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
};
diff --git a/init/epoll.cpp b/init/epoll.cpp
new file mode 100644
index 0000000..4bca09e
--- /dev/null
+++ b/init/epoll.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.
+ */
+
+#include "epoll.h"
+
+#include <sys/epoll.h>
+
+#include <chrono>
+#include <functional>
+#include <map>
+
+namespace android {
+namespace init {
+
+Epoll::Epoll() {}
+
+Result<Success> Epoll::Open() {
+ if (epoll_fd_ >= 0) return Success();
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+ if (epoll_fd_ == -1) {
+ return ErrnoError() << "epoll_create1 failed";
+ }
+ return Success();
+}
+
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler) {
+ 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;
+ // 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";
+ epoll_handlers_.erase(fd);
+ return result;
+ }
+ return Success();
+}
+
+Result<Success> 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();
+}
+
+Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
+ int timeout_ms = -1;
+ if (timeout && timeout->count() < INT_MAX) {
+ timeout_ms = timeout->count();
+ }
+ epoll_event ev;
+ auto nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, &ev, 1, timeout_ms));
+ if (nr == -1) {
+ return ErrnoError() << "epoll_wait failed";
+ } else if (nr == 1) {
+ std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
+ }
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/epoll.h b/init/epoll.h
new file mode 100644
index 0000000..85a791c
--- /dev/null
+++ b/init/epoll.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _INIT_EPOLL_H
+#define _INIT_EPOLL_H
+
+#include <chrono>
+#include <functional>
+#include <map>
+#include <optional>
+
+#include <android-base/unique_fd.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+class Epoll {
+ 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);
+
+ private:
+ android::base::unique_fd epoll_fd_;
+ std::map<int, std::function<void()>> epoll_handlers_;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
new file mode 100644
index 0000000..faf6fc1
--- /dev/null
+++ b/init/host_import_parser.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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_import_parser.h"
+
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
+ const std::string& filename, int line) {
+ if (args.size() != 2) {
+ return Error() << "single argument needed for import\n";
+ }
+
+ auto import_path = args[1];
+
+ if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
+ StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
+ import_path = out_dir_ + "/" + import_path;
+ } else {
+ import_path = out_dir_ + "/root/" + import_path;
+ }
+
+ return ImportParser::ParseSection({"import", import_path}, filename, line);
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
new file mode 100644
index 0000000..e2980b2
--- /dev/null
+++ b/init/host_import_parser.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <vector>
+
+#include "import_parser.h"
+#include "parser.h"
+
+namespace android {
+namespace init {
+
+class HostImportParser : public ImportParser {
+ public:
+ HostImportParser(const std::string& out_dir, Parser* parser)
+ : ImportParser(parser), out_dir_(out_dir) {}
+ Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line) override;
+
+ private:
+ std::string out_dir_;
+};
+
+} // namespace init
+} // namespace android
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
deleted file mode 100644
index 5232b7e..0000000
--- a/init/host_init_parser.cpp
+++ /dev/null
@@ -1,82 +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 <pwd.h>
-
-#include <android-base/logging.h>
-
-#include "action.h"
-#include "action_manager.h"
-#include "action_parser.h"
-#include "parser.h"
-#include "result.h"
-#include "service.h"
-
-// The host passwd file won't have the Android entries, so we fake success here.
-passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
- char dummy_buf[] = "dummy";
- static passwd dummy_passwd = {
- .pw_name = dummy_buf,
- .pw_dir = dummy_buf,
- .pw_shell = dummy_buf,
- .pw_uid = 123,
- .pw_gid = 123,
- };
- return &dummy_passwd;
-}
-
-namespace android {
-namespace init {
-
-static Result<Success> do_stub(const BuiltinArguments& args) {
- return Success();
-}
-
-#include "generated_stub_builtin_function_map.h"
-
-int main(int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
- if (argc != 2) {
- LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
- return -1;
- }
- const BuiltinFunctionMap function_map;
- 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("on", std::make_unique<ActionParser>(&am, nullptr));
-
- size_t num_errors = 0;
- if (!parser.ParseConfig(argv[1], &num_errors)) {
- LOG(ERROR) << "Failed to find script";
- return -1;
- }
- if (num_errors > 0) {
- LOG(ERROR) << "Parse failed with " << num_errors << " errors";
- return -1;
- }
- LOG(INFO) << "Parse success!";
- return 0;
-}
-
-} // namespace init
-} // namespace android
-
-int main(int argc, char** argv) {
- android::init::main(argc, argv);
-}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
index e6cc08a9..2352fc7 100644
--- a/init/host_init_stubs.cpp
+++ b/init/host_init_stubs.cpp
@@ -16,39 +16,35 @@
#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 base {
-
-std::string GetProperty(const std::string&, const std::string& default_value) {
- return default_value;
-}
-
-bool GetBoolProperty(const std::string&, bool default_value) {
- return default_value;
-}
-
-} // namespace base
-} // namespace android
-
-namespace android {
namespace init {
// init.h
std::string default_console = "/dev/console";
// property_service.h
-uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+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
+bool SelinuxHasVendorInit() {
+ return true;
+}
+
void SelabelInitialize() {}
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index ddfb7ae..f0e1f07 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,21 +29,6 @@
// unistd.h
int setgroups(size_t __size, const gid_t* __list);
-// android-base/properties.h
-namespace android {
-namespace base {
-
-std::string GetProperty(const std::string& key, const std::string& default_value);
-bool GetBoolProperty(const std::string& key, bool default_value);
-template <typename T>
-T GetIntProperty(const std::string&, T default_value, T = std::numeric_limits<T>::min(),
- T = std::numeric_limits<T>::max()) {
- return default_value;
-}
-
-} // namespace base
-} // namespace android
-
namespace android {
namespace init {
@@ -56,6 +41,7 @@
const std::string& source_context, const ucred& cr, std::string* error);
// selinux.h
+bool SelinuxHasVendorInit();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
new file mode 100644
index 0000000..d6884af
--- /dev/null
+++ b/init/host_init_verifier.cpp
@@ -0,0 +1,162 @@
+//
+// 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 <pwd.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "host_import_parser.h"
+#include "host_init_stubs.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+#define EXCLUDE_FS_CONFIG_STRUCTURES
+#include "generated_android_ids.h"
+
+using namespace std::literals;
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+using android::base::Split;
+
+static std::string out_dir;
+
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
+ std::string passwd;
+ if (!ReadFileToString(out_dir + "/vendor/etc/passwd", &passwd)) {
+ return {};
+ }
+
+ std::vector<std::pair<std::string, int>> result;
+ auto passwd_lines = Split(passwd, "\n");
+ for (const auto& line : passwd_lines) {
+ auto split_line = Split(line, ":");
+ if (split_line.size() < 3) {
+ continue;
+ }
+ int uid = 0;
+ if (!ParseInt(split_line[2], &uid)) {
+ continue;
+ }
+ result.emplace_back(split_line[0], uid);
+ }
+ 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] = "";
+ static char static_dir[32] = "/";
+ static char static_shell[32] = "/system/bin/sh";
+ static passwd static_passwd = {
+ .pw_name = static_name,
+ .pw_dir = static_dir,
+ .pw_shell = static_shell,
+ .pw_uid = 0,
+ .pw_gid = 0,
+ };
+
+ for (size_t n = 0; n < android_id_count; ++n) {
+ if (!strcmp(android_ids[n].name, login)) {
+ snprintf(static_name, sizeof(static_name), "%s", android_ids[n].name);
+ static_passwd.pw_uid = android_ids[n].aid;
+ static_passwd.pw_gid = android_ids[n].aid;
+ return &static_passwd;
+ }
+ }
+
+ static const auto vendor_passwd = GetVendorPasswd();
+
+ for (const auto& [name, uid] : vendor_passwd) {
+ if (name == login) {
+ snprintf(static_name, sizeof(static_name), "%s", name.c_str());
+ static_passwd.pw_uid = uid;
+ static_passwd.pw_gid = uid;
+ return &static_passwd;
+ }
+ }
+
+ errno = ENOENT;
+ return nullptr;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+ return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StdioLogger);
+ android::base::SetMinimumLogSeverity(android::base::ERROR);
+ if (argc != 3) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
+ return -1;
+ }
+
+ out_dir = argv[1];
+
+ auto properties = Split(argv[2], ",");
+ for (const auto& property : properties) {
+ auto split_property = Split(property, "=");
+ if (split_property.size() != 2) {
+ continue;
+ }
+ property_set(split_property[0], split_property[1]);
+ }
+
+ const BuiltinFunctionMap function_map;
+ 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("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("import", std::make_unique<HostImportParser>(out_dir, &parser));
+
+ if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
+ LOG(ERROR) << "Failed to find root init.rc script";
+ return -1;
+ }
+ if (parser.parse_error_count() > 0) {
+ LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index e335fd1..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -41,14 +41,15 @@
return Success();
}
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+ return Error() << "Unexpected line found after import statement";
+}
+
void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
for (const auto& [import, line_num] : current_imports) {
- if (!parser_->ParseConfig(import)) {
- PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
- << "'";
- }
+ parser_->ParseConfig(import);
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 5a2f894..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -30,6 +30,7 @@
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;
void EndFile() override;
private:
diff --git a/init/init.cpp b/init/init.cpp
index 0d5690b..82648d9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -24,13 +24,16 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/epoll.h>
#include <sys/mount.h>
#include <sys/signalfd.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
+#include <map>
+#include <memory>
+#include <optional>
+
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -43,10 +46,8 @@
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <memory>
-#include <optional>
-
#include "action_parser.h"
+#include "epoll.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "keychords.h"
@@ -60,6 +61,7 @@
#include "util.h"
#include "watchdogd.h"
+using namespace std::chrono_literals;
using namespace std::string_literals;
using android::base::boot_clock;
@@ -78,7 +80,6 @@
std::string default_console = "/dev/console";
-static int epoll_fd = -1;
static int signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -130,15 +131,6 @@
}
}
-void register_epoll_handler(int fd, void (*fn)()) {
- epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.ptr = reinterpret_cast<void*>(fn);
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- PLOG(ERROR) << "epoll_ctl failed";
- }
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
@@ -277,40 +269,29 @@
const ControlMessageFunction& function = it->second;
- if (function.target == ControlTarget::SERVICE) {
- Service* svc = ServiceList::GetInstance().FindService(name);
- if (svc == nullptr) {
- LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
- return;
- }
- if (auto result = function.action(svc); !result) {
- LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
- << result.error();
- }
+ Service* svc = nullptr;
+ switch (function.target) {
+ case ControlTarget::SERVICE:
+ svc = ServiceList::GetInstance().FindService(name);
+ break;
+ case ControlTarget::INTERFACE:
+ svc = ServiceList::GetInstance().FindInterface(name);
+ break;
+ default:
+ LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
+ << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
+ return;
+ }
+
+ if (svc == nullptr) {
+ LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
return;
}
- if (function.target == ControlTarget::INTERFACE) {
- for (const auto& svc : ServiceList::GetInstance()) {
- if (svc->interfaces().count(name) == 0) {
- continue;
- }
-
- if (auto result = function.action(svc.get()); !result) {
- LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
- << " with interface " << name << ": " << result.error();
- }
-
- return;
- }
-
- LOG(ERROR) << "Could not find service hosting interface " << name;
- return;
+ if (auto result = function.action(svc); !result) {
+ LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
}
-
- LOG(ERROR) << "Invalid function target from static map key '" << msg
- << "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
}
static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
@@ -334,11 +315,6 @@
return Success();
}
-static Result<Success> keychord_init_action(const BuiltinArguments& args) {
- keychord_init();
- return Success();
-}
-
static Result<Success> console_init_action(const BuiltinArguments& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
@@ -541,7 +517,7 @@
}
}
-static void InstallSignalFdHandler() {
+static void InstallSignalFdHandler(Epoll* epoll) {
// Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
// SIGCHLD when a child process stops or continues (b/77867680#comment9).
const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
@@ -572,7 +548,36 @@
PLOG(FATAL) << "failed to create signalfd";
}
- register_epoll_handler(signal_fd, HandleSignalFd);
+ if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result) {
+ LOG(FATAL) << result.error();
+ }
+}
+
+void HandleKeychord(const std::vector<int>& keycodes) {
+ // Only handle keychords if adb is enabled.
+ std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
+ if (adb_enabled != "running") {
+ LOG(WARNING) << "Not starting service for keychord " << android::base::Join(keycodes, ' ')
+ << " because ADB is disabled";
+ return;
+ }
+
+ auto found = false;
+ for (const auto& service : ServiceList::GetInstance()) {
+ auto svc = service.get();
+ if (svc->keycodes() == keycodes) {
+ found = true;
+ LOG(INFO) << "Starting service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ');
+ if (auto result = svc->Start(); !result) {
+ LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord "
+ << android::base::Join(keycodes, ' ') << ": " << result.error();
+ }
+ }
+ }
+ if (!found) {
+ LOG(ERROR) << "Service for keychord " << android::base::Join(keycodes, ' ') << " not found";
+ }
}
int main(int argc, char** argv) {
@@ -718,16 +723,16 @@
SelabelInitialize();
SelinuxRestoreContext();
- epoll_fd = epoll_create1(EPOLL_CLOEXEC);
- if (epoll_fd == -1) {
- PLOG(FATAL) << "epoll_create1 failed";
+ Epoll epoll;
+ if (auto result = epoll.Open(); !result) {
+ PLOG(FATAL) << result.error();
}
- InstallSignalFdHandler();
+ InstallSignalFdHandler(&epoll);
property_load_boot_defaults();
export_oem_lock_status();
- start_property_service();
+ StartPropertyService(&epoll);
set_usb_controller();
const BuiltinFunctionMap function_map;
@@ -752,7 +757,16 @@
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
- am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+ 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");
// Trigger all the boot actions to get us started.
@@ -775,7 +789,7 @@
while (true) {
// By default, sleep until something happens.
- int epoll_timeout_ms = -1;
+ auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
if (do_shutdown && !shutting_down) {
do_shutdown = false;
@@ -793,23 +807,18 @@
// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
- epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
- *next_process_restart_time - boot_clock::now())
- .count();
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+ *next_process_restart_time - boot_clock::now());
+ if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
// If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
- epoll_event ev;
- int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
- if (nr == -1) {
- PLOG(ERROR) << "epoll_wait failed";
- } else if (nr == 1) {
- ((void (*)()) ev.data.ptr)();
+ if (auto result = epoll.Wait(epoll_timeout); !result) {
+ LOG(ERROR) << result.error();
}
}
diff --git a/init/init.h b/init/init.h
index d4a0e96..6c82fa1 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
+#include <functional>
#include <string>
#include <vector>
@@ -42,8 +43,6 @@
void property_changed(const std::string& name, const std::string& value);
-void register_epoll_handler(int fd, void (*fn)());
-
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 1b6e97e..db60ce1 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -33,11 +33,13 @@
#include "devices.h"
#include "fs_mgr.h"
#include "fs_mgr_avb.h"
+#include "fs_mgr_dm_linear.h"
#include "uevent.h"
#include "uevent_listener.h"
#include "util.h"
using android::base::Timer;
+using android::fs_mgr::LogicalPartitionTable;
namespace android {
namespace init {
@@ -58,21 +60,24 @@
protected:
ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
bool InitRequiredDevices();
- bool InitVerityDevice(const std::string& verity_device);
+ bool InitMappedDevice(const std::string& verity_device);
+ bool CreateLogicalPartitions();
bool MountPartitions();
+ bool GetBackingDmLinearDevices();
virtual ListenerAction UeventCallback(const Uevent& uevent);
// Pure virtual functions.
- virtual bool GetRequiredDevices() = 0;
+ virtual bool GetDmVerityDevices() = 0;
virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
bool need_dm_verity_;
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+ std::unique_ptr<LogicalPartitionTable> dm_linear_table_;
std::vector<fstab_rec*> mount_fstab_recs_;
std::set<std::string> required_devices_partition_names_;
- DeviceHandler device_handler_;
+ std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
@@ -82,7 +87,7 @@
~FirstStageMountVBootV1() override = default;
protected:
- bool GetRequiredDevices() override;
+ bool GetDmVerityDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
};
@@ -95,7 +100,7 @@
protected:
ListenerAction UeventCallback(const Uevent& uevent) override;
- bool GetRequiredDevices() override;
+ bool GetDmVerityDevices() override;
bool SetUpDmVerity(fstab_rec* fstab_rec) override;
bool InitAvbHandle();
@@ -114,6 +119,17 @@
return access("/sbin/recovery", F_OK) == 0;
}
+static inline bool IsDmLinearEnabled() {
+ bool enabled = false;
+ import_kernel_cmdline(
+ false, [&enabled](const std::string& key, const std::string& value, bool in_qemu) {
+ if (key == "androidboot.logical_partitions" && value == "1") {
+ enabled = true;
+ }
+ });
+ return enabled;
+}
+
// Class Definitions
// -----------------
FirstStageMount::FirstStageMount()
@@ -127,6 +143,15 @@
} else {
LOG(INFO) << "Failed to read fstab from device tree";
}
+
+ if (IsDmLinearEnabled()) {
+ dm_linear_table_ = android::fs_mgr::LoadPartitionsFromDeviceTree();
+ }
+
+ auto boot_devices = fs_mgr_get_boot_devices();
+ device_handler_ =
+ std::make_unique<DeviceHandler>(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
+ std::vector<Subsystem>{}, std::move(boot_devices), false);
}
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
@@ -138,7 +163,7 @@
}
bool FirstStageMount::DoFirstStageMount() {
- if (mount_fstab_recs_.empty()) {
+ if (!dm_linear_table_ && mount_fstab_recs_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
@@ -146,13 +171,30 @@
if (!InitDevices()) return false;
+ if (!CreateLogicalPartitions()) return false;
+
if (!MountPartitions()) return false;
return true;
}
bool FirstStageMount::InitDevices() {
- return GetRequiredDevices() && InitRequiredDevices();
+ return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+}
+
+bool FirstStageMount::GetBackingDmLinearDevices() {
+ // Add any additional devices required for dm-linear mappings.
+ if (!dm_linear_table_) {
+ return true;
+ }
+
+ for (const auto& partition : dm_linear_table_->partitions) {
+ for (const auto& extent : partition.extents) {
+ const std::string& partition_name = android::base::Basename(extent.block_device());
+ required_devices_partition_names_.emplace(partition_name);
+ }
+ }
+ return true;
}
// Creates devices with uevent->partition_name matching one in the member variable
@@ -163,12 +205,12 @@
return true;
}
- if (need_dm_verity_) {
+ if (dm_linear_table_ || 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_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -210,6 +252,13 @@
return true;
}
+bool FirstStageMount::CreateLogicalPartitions() {
+ if (!dm_linear_table_) {
+ return true;
+ }
+ return android::fs_mgr::CreateLogicalPartitions(*dm_linear_table_.get());
+}
+
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
// Matches partition name to create device nodes.
// Both required_devices_partition_names_ and uevent->partition_name have A/B
@@ -218,7 +267,7 @@
if (iter != required_devices_partition_names_.end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
required_devices_partition_names_.erase(iter);
- device_handler_.HandleDeviceEvent(uevent);
+ device_handler_->HandleDeviceEvent(uevent);
if (required_devices_partition_names_.empty()) {
return ListenerAction::kStop;
} else {
@@ -247,15 +296,15 @@
}
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-bool FirstStageMount::InitVerityDevice(const std::string& verity_device) {
- const std::string device_name(basename(verity_device.c_str()));
+bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
+ const std::string device_name(basename(dm_device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
bool found = false;
- auto verity_callback = [&device_name, &verity_device, this, &found](const Uevent& uevent) {
+ auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
- LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
- device_handler_.HandleDeviceEvent(uevent);
+ LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
+ device_handler_->HandleDeviceEvent(uevent);
found = true;
return ListenerAction::kStop;
}
@@ -279,6 +328,14 @@
bool FirstStageMount::MountPartitions() {
for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_logical(fstab_rec)) {
+ if (!fs_mgr_update_logical_partition(fstab_rec)) {
+ return false;
+ }
+ if (!InitMappedDevice(fstab_rec->blk_device)) {
+ return false;
+ }
+ }
if (!SetUpDmVerity(fstab_rec)) {
PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
return false;
@@ -291,7 +348,7 @@
return true;
}
-bool FirstStageMountVBootV1::GetRequiredDevices() {
+bool FirstStageMountVBootV1::GetDmVerityDevices() {
std::string verity_loc_device;
need_dm_verity_ = false;
@@ -344,7 +401,7 @@
// 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 InitVerityDevice(fstab_rec->blk_device);
+ return InitMappedDevice(fstab_rec->blk_device);
default:
return false;
}
@@ -371,7 +428,7 @@
}
}
-bool FirstStageMountVBootV2::GetRequiredDevices() {
+bool FirstStageMountVBootV2::GetDmVerityDevices() {
need_dm_verity_ = false;
// fstab_rec->blk_device has A/B suffix.
@@ -416,9 +473,8 @@
// 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/by-num/p1
// - /dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1
- std::vector<std::string> links = device_handler_.GetBlockDeviceSymlinks(uevent);
+ 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) {
@@ -444,7 +500,7 @@
// 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 InitVerityDevice(fstab_rec->blk_device);
+ return InitMappedDevice(fstab_rec->blk_device);
default:
return false;
}
diff --git a/init/keychords.cpp b/init/keychords.cpp
index e686ce1..1af06dd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -16,110 +16,269 @@
#include "keychords.h"
+#include <dirent.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
+#include <linux/input.h>
+#include <sys/cdefs.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
-#include <linux/keychord.h>
#include <unistd.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
-#include "init.h"
+#include <android-base/logging.h>
namespace android {
namespace init {
-static struct input_keychord *keychords = 0;
-static int keychords_count = 0;
-static int keychords_length = 0;
-static int keychord_fd = -1;
+Keychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}
-void add_service_keycodes(Service* svc)
-{
- struct input_keychord *keychord;
- size_t i, size;
-
- if (!svc->keycodes().empty()) {
- /* add a new keychord to the list */
- size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
- keychords = (input_keychord*) realloc(keychords, keychords_length + size);
- if (!keychords) {
- PLOG(ERROR) << "could not allocate keychords";
- keychords_length = 0;
- keychords_count = 0;
- return;
- }
-
- keychord = (struct input_keychord *)((char *)keychords + keychords_length);
- keychord->version = KEYCHORD_VERSION;
- keychord->id = keychords_count + 1;
- keychord->count = svc->keycodes().size();
- svc->set_keychord_id(keychord->id);
-
- for (i = 0; i < svc->keycodes().size(); i++) {
- keychord->keycodes[i] = svc->keycodes()[i];
- }
- keychords_count++;
- keychords_length += size;
+Keychords::~Keychords() noexcept {
+ if (inotify_fd_ >= 0) {
+ epoll_->UnregisterHandler(inotify_fd_);
+ ::close(inotify_fd_);
}
+ while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
}
-static void handle_keychord() {
- int ret;
- __u16 id;
+Keychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
- ret = read(keychord_fd, &id, sizeof(id));
- if (ret != sizeof(id)) {
- PLOG(ERROR) << "could not read keychord id";
- return;
- }
-
- // Only handle keychords if adb is enabled.
- std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
- if (adb_enabled == "running") {
- Service* svc = ServiceList::GetInstance().FindService(id, &Service::keychord_id);
- if (svc) {
- LOG(INFO) << "Starting service '" << svc->name() << "' from keychord " << id;
- if (auto result = svc->Start(); !result) {
- LOG(ERROR) << "Could not start service '" << svc->name() << "' from keychord " << id
- << ": " << result.error();
- }
- } else {
- LOG(ERROR) << "Service for keychord " << id << " not found";
- }
+void Keychords::Mask::SetBit(size_t bit, bool value) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) return;
+ if (value) {
+ bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));
} else {
- LOG(WARNING) << "Not starting service for keychord " << id << " because ADB is disabled";
+ bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
}
}
-void keychord_init() {
- for (const auto& service : ServiceList::GetInstance()) {
- add_service_keycodes(service.get());
+bool Keychords::Mask::GetBit(size_t bit) const {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));
+}
+
+size_t Keychords::Mask::bytesize() const {
+ return bits_.size() * sizeof(mask_t);
+}
+
+void* Keychords::Mask::data() {
+ return bits_.data();
+}
+
+size_t Keychords::Mask::size() const {
+ return bits_.size() * sizeof(mask_t) * kBitsPerByte;
+}
+
+void Keychords::Mask::resize(size_t bit) {
+ auto idx = bit / (kBitsPerByte * sizeof(mask_t));
+ if (idx >= bits_.size()) {
+ bits_.resize(idx + 1, 0);
+ }
+}
+
+Keychords::Mask::operator bool() const {
+ for (size_t i = 0; i < bits_.size(); ++i) {
+ if (bits_[i]) return true;
+ }
+ return false;
+}
+
+Keychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {
+ auto len = std::min(bits_.size(), rval.bits_.size());
+ Keychords::Mask ret;
+ ret.bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ ret.bits_[i] = bits_[i] & rval.bits_[i];
+ }
+ return ret;
+}
+
+void Keychords::Mask::operator|=(const Keychords::Mask& rval) {
+ auto len = rval.bits_.size();
+ bits_.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ bits_[i] |= rval.bits_[i];
+ }
+}
+
+Keychords::Entry::Entry() : notified(false) {}
+
+void Keychords::LambdaCheck() {
+ for (auto& [keycodes, entry] : entries_) {
+ auto found = true;
+ for (auto& code : keycodes) {
+ if (!current_.GetBit(code)) {
+ entry.notified = false;
+ found = false;
+ break;
+ }
+ }
+ if (!found) continue;
+ if (entry.notified) continue;
+ entry.notified = true;
+ handler_(keycodes);
+ }
+}
+
+void Keychords::LambdaHandler(int fd) {
+ input_event event;
+ auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
+ if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
+ current_.SetBit(event.code, event.value);
+ LambdaCheck();
+}
+
+bool Keychords::GeteventEnable(int fd) {
+ // Make sure it is an event channel, should pass this ioctl call
+ int version;
+ if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+
+#ifdef EVIOCSMASK
+ static auto EviocsmaskSupported = true;
+ if (EviocsmaskSupported) {
+ Keychords::Mask mask(EV_KEY);
+ mask.SetBit(EV_KEY);
+ input_mask msg = {};
+ msg.type = EV_SYN;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ if (::ioctl(fd, EVIOCSMASK, &msg) == -1) {
+ PLOG(WARNING) << "EVIOCSMASK not supported";
+ EviocsmaskSupported = false;
+ }
+ }
+#endif
+
+ Keychords::Mask mask;
+ for (auto& [keycodes, entry] : entries_) {
+ for (auto& code : keycodes) {
+ mask.resize(code);
+ mask.SetBit(code);
+ }
}
- // Nothing to do if no services require keychords.
- if (!keychords) {
+ current_.resize(mask.size());
+ Keychords::Mask available(mask.size());
+ auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
+ if (res == -1) return false;
+ if (!(available & mask)) return false;
+
+#ifdef EVIOCSMASK
+ if (EviocsmaskSupported) {
+ input_mask msg = {};
+ msg.type = EV_KEY;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ ::ioctl(fd, EVIOCSMASK, &msg);
+ }
+#endif
+
+ Keychords::Mask set(mask.size());
+ res = ::ioctl(fd, EVIOCGKEY(res), set.data());
+ if (res > 0) {
+ current_ |= mask & available & set;
+ LambdaCheck();
+ }
+ epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });
+ return true;
+}
+
+void Keychords::GeteventOpenDevice(const std::string& device) {
+ if (registration_.count(device)) return;
+ auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Can not open " << device;
+ return;
+ }
+ if (!GeteventEnable(fd)) {
+ ::close(fd);
+ } else {
+ registration_.emplace(device, fd);
+ }
+}
+
+void Keychords::GeteventCloseDevice(const std::string& device) {
+ auto it = registration_.find(device);
+ if (it == registration_.end()) return;
+ auto fd = (*it).second;
+ epoll_->UnregisterHandler(fd);
+ registration_.erase(it);
+ ::close(fd);
+}
+
+void Keychords::InotifyHandler() {
+ unsigned char buf[512]; // History shows 32-64 bytes typical
+
+ auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));
+ if (res < 0) {
+ PLOG(WARNING) << "could not get event";
return;
}
- keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
- if (keychord_fd == -1) {
- PLOG(ERROR) << "could not open /dev/keychord";
- return;
+ auto event_buf = buf;
+ while (static_cast<size_t>(res) >= sizeof(inotify_event)) {
+ auto event = reinterpret_cast<inotify_event*>(event_buf);
+ auto event_size = sizeof(inotify_event) + event->len;
+ if (static_cast<size_t>(res) < event_size) break;
+ if (event->len) {
+ std::string devname(kDevicePath);
+ devname += '/';
+ devname += event->name;
+ if (event->mask & IN_CREATE) {
+ GeteventOpenDevice(devname);
+ } else {
+ GeteventCloseDevice(devname);
+ }
+ }
+ res -= event_size;
+ event_buf += event_size;
+ }
+}
+
+void Keychords::GeteventOpenDevice() {
+ inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (inotify_fd_ < 0) {
+ PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
+ } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <
+ 0) {
+ PLOG(WARNING) << "Could not add watch for " << kDevicePath;
+ ::close(inotify_fd_);
+ inotify_fd_ = -1;
}
- int ret = write(keychord_fd, keychords, keychords_length);
- if (ret != keychords_length) {
- PLOG(ERROR) << "could not configure /dev/keychord " << ret;
- close(keychord_fd);
+ std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+ if (device) {
+ dirent* entry;
+ while ((entry = readdir(device.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname(kDevicePath);
+ devname += '/';
+ devname += entry->d_name;
+ GeteventOpenDevice(devname);
+ }
}
- free(keychords);
- keychords = nullptr;
+ if (inotify_fd_ >= 0) {
+ epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });
+ }
+}
- register_epoll_handler(keychord_fd, handle_keychord);
+void Keychords::Register(const std::vector<int>& keycodes) {
+ if (keycodes.empty()) return;
+ entries_.try_emplace(keycodes, Entry());
+}
+
+void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
+ epoll_ = epoll;
+ handler_ = handler;
+ if (entries_.size()) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 1c34098..00ed205 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -17,13 +17,81 @@
#ifndef _INIT_KEYCHORDS_H_
#define _INIT_KEYCHORDS_H_
-#include "service.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "epoll.h"
namespace android {
namespace init {
-void add_service_keycodes(Service* svc);
-void keychord_init();
+class Keychords {
+ public:
+ Keychords();
+ Keychords(const Keychords&) = delete;
+ Keychords(Keychords&&) = delete;
+ Keychords& operator=(const Keychords&) = delete;
+ Keychords& operator=(Keychords&&) = delete;
+ ~Keychords() noexcept;
+
+ void Register(const std::vector<int>& keycodes);
+ void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);
+
+ private:
+ // Bit management
+ class Mask {
+ public:
+ explicit Mask(size_t bit = 0);
+
+ void SetBit(size_t bit, bool value = true);
+ bool GetBit(size_t bit) const;
+
+ size_t bytesize() const;
+ void* data();
+ size_t size() const;
+ void resize(size_t bit);
+
+ operator bool() const;
+ Mask operator&(const Mask& rval) const;
+ void operator|=(const Mask& rval);
+
+ private:
+ typedef unsigned int mask_t;
+ static constexpr size_t kBitsPerByte = 8;
+
+ std::vector<mask_t> bits_;
+ };
+
+ struct Entry {
+ Entry();
+
+ bool notified;
+ };
+
+ static constexpr char kDevicePath[] = "/dev/input";
+
+ void LambdaCheck();
+ void LambdaHandler(int fd);
+ void InotifyHandler();
+
+ bool GeteventEnable(int fd);
+ void GeteventOpenDevice(const std::string& device);
+ void GeteventOpenDevice();
+ void GeteventCloseDevice(const std::string& device);
+
+ Epoll* epoll_;
+ std::function<void(const std::vector<int>&)> handler_;
+
+ std::map<std::string, int> registration_;
+
+ std::map<const std::vector<int>, Entry> entries_;
+
+ Mask current_;
+
+ int inotify_fd_;
+};
} // namespace init
} // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
new file mode 100644
index 0000000..c8c47a8
--- /dev/null
+++ b/init/keychords_test.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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 "keychords.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/uinput.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "epoll.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+// This class is used to inject keys.
+class EventHandler {
+ public:
+ EventHandler();
+ EventHandler(const EventHandler&) = delete;
+ EventHandler(EventHandler&&);
+ EventHandler& operator=(const EventHandler&) = delete;
+ EventHandler& operator=(EventHandler&&);
+ ~EventHandler() noexcept;
+
+ bool init();
+
+ bool send(struct input_event& e);
+ bool send(uint16_t type, uint16_t code, uint16_t value);
+ bool send(uint16_t code, bool value);
+
+ private:
+ int fd_;
+};
+
+EventHandler::EventHandler() : fd_(-1) {}
+
+EventHandler::EventHandler(EventHandler&& rval) : fd_(rval.fd_) {
+ rval.fd_ = -1;
+}
+
+EventHandler& EventHandler::operator=(EventHandler&& rval) {
+ fd_ = rval.fd_;
+ rval.fd_ = -1;
+ return *this;
+}
+
+EventHandler::~EventHandler() {
+ if (fd_ == -1) return;
+ ::ioctl(fd_, UI_DEV_DESTROY);
+ ::close(fd_);
+}
+
+bool EventHandler::init() {
+ if (fd_ != -1) return true;
+ auto fd = TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd == -1) return false;
+ if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {
+ ::close(fd);
+ return false;
+ }
+
+ static const struct uinput_user_dev u = {
+ .name = "com.google.android.init.test",
+ .id.bustype = BUS_VIRTUAL,
+ .id.vendor = 0x1AE0, // Google
+ .id.product = 0x494E, // IN
+ .id.version = 1,
+ };
+ if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {
+ ::close(fd);
+ return false;
+ }
+
+ // all keys
+ for (uint16_t i = 0; i < KEY_MAX; ++i) {
+ if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {
+ ::close(fd);
+ return false;
+ }
+ }
+ if (::ioctl(fd, UI_DEV_CREATE) == -1) {
+ ::close(fd);
+ return false;
+ }
+ fd_ = fd;
+ return true;
+}
+
+bool EventHandler::send(struct input_event& e) {
+ gettimeofday(&e.time, nullptr);
+ return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);
+}
+
+bool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {
+ struct input_event e = {.type = type, .code = code, .value = value};
+ return send(e);
+}
+
+bool EventHandler::send(uint16_t code, bool value) {
+ return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);
+}
+
+std::string InitFds(const char* prefix, pid_t pid = getpid()) {
+ std::string ret;
+
+ std::string init_fds("/proc/");
+ init_fds += std::to_string(pid) + "/fd";
+ std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);
+ if (!fds) return ret;
+
+ dirent* entry;
+ while ((entry = readdir(fds.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname = init_fds + '/' + entry->d_name;
+ char buf[256];
+ auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);
+ if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;
+ buf[retval] = '\0';
+ if (!android::base::StartsWith(buf, prefix)) continue;
+ if (ret.size() != 0) ret += ",";
+ ret += buf;
+ }
+ return ret;
+}
+
+std::string InitInputFds() {
+ return InitFds("/dev/input/");
+}
+
+std::string InitInotifyFds() {
+ return InitFds("anon_inode:inotify");
+}
+
+// NB: caller (this series of tests, or conversely the service parser in init)
+// is responsible for validation, sorting and uniqueness of the chords, so no
+// fuzzing is advised.
+
+const std::vector<int> escape_chord = {KEY_ESC};
+const std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};
+const std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};
+
+const std::vector<const std::vector<int>> empty_chords;
+const std::vector<const std::vector<int>> chords = {
+ escape_chord,
+ triple1_chord,
+ triple2_chord,
+};
+
+class TestFrame {
+ public:
+ TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev = nullptr);
+
+ void RelaxForMs(std::chrono::milliseconds wait = 1ms);
+
+ void SetChord(int key, bool value = true);
+ void SetChords(const std::vector<int>& chord, bool value = true);
+ void ClrChord(int key);
+ void ClrChords(const std::vector<int>& chord);
+
+ bool IsOnlyChord(const std::vector<int>& chord) const;
+ bool IsNoChord() const;
+ bool IsChord(const std::vector<int>& chord) const;
+ void WaitForChord(const std::vector<int>& chord);
+
+ std::string Format() const;
+
+ private:
+ static std::string Format(const std::vector<const std::vector<int>>& chords);
+
+ Epoll epoll_;
+ Keychords keychords_;
+ std::vector<const std::vector<int>> keycodes_;
+ EventHandler* ev_;
+};
+
+TestFrame::TestFrame(const std::vector<const std::vector<int>>& chords, EventHandler* ev)
+ : ev_(ev) {
+ if (!epoll_.Open()) return;
+ for (const auto& keycodes : chords) keychords_.Register(keycodes);
+ keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {
+ this->keycodes_.emplace_back(keycodes);
+ });
+}
+
+void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
+ epoll_.Wait(wait);
+}
+
+void TestFrame::SetChord(int key, bool value) {
+ ASSERT_TRUE(!!ev_);
+ RelaxForMs();
+ EXPECT_TRUE(ev_->send(key, value));
+}
+
+void TestFrame::SetChords(const std::vector<int>& chord, bool value) {
+ ASSERT_TRUE(!!ev_);
+ for (auto& key : chord) SetChord(key, value);
+ RelaxForMs();
+}
+
+void TestFrame::ClrChord(int key) {
+ ASSERT_TRUE(!!ev_);
+ SetChord(key, false);
+}
+
+void TestFrame::ClrChords(const std::vector<int>& chord) {
+ ASSERT_TRUE(!!ev_);
+ SetChords(chord, false);
+}
+
+bool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {
+ auto ret = false;
+ for (const auto& keycode : keycodes_) {
+ if (keycode != chord) return false;
+ ret = true;
+ }
+ return ret;
+}
+
+bool TestFrame::IsNoChord() const {
+ return keycodes_.empty();
+}
+
+bool TestFrame::IsChord(const std::vector<int>& chord) const {
+ for (const auto& keycode : keycodes_) {
+ if (keycode == chord) return true;
+ }
+ return false;
+}
+
+void TestFrame::WaitForChord(const std::vector<int>& chord) {
+ for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();
+}
+
+std::string TestFrame::Format(const std::vector<const std::vector<int>>& chords) {
+ std::string ret("{");
+ if (!chords.empty()) {
+ ret += android::base::Join(chords.front(), ' ');
+ for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {
+ ret += ',';
+ ret += android::base::Join(*it, ' ');
+ }
+ }
+ return ret + '}';
+}
+
+std::string TestFrame::Format() const {
+ return Format(keycodes_);
+}
+
+} // namespace
+
+TEST(keychords, not_instantiated) {
+ TestFrame test_frame(empty_chords);
+ EXPECT_TRUE(InitInotifyFds().size() == 0);
+}
+
+TEST(keychords, instantiated) {
+ // Test if a valid set of chords results in proper instantiation of the
+ // underlying mechanisms for /dev/input/ attachment.
+ TestFrame test_frame(chords);
+ EXPECT_TRUE(InitInotifyFds().size() != 0);
+}
+
+TEST(keychords, init_inotify) {
+ std::string before(InitInputFds());
+
+ TestFrame test_frame(chords);
+
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+
+ for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();
+ std::string after(InitInputFds());
+ EXPECT_NE(before, after);
+}
+
+TEST(keychords, key) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(escape_chord);
+ test_frame.WaitForChord(escape_chord);
+ test_frame.ClrChords(escape_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))
+ << "expected only " << android::base::Join(escape_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+TEST(keychords, keys_in_series) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ for (auto& key : triple1_chord) {
+ test_frame.SetChord(key);
+ test_frame.ClrChord(key);
+ }
+ test_frame.WaitForChord(triple1_chord);
+ EXPECT_TRUE(test_frame.IsNoChord()) << "expected nothing got " << test_frame.Format();
+}
+
+TEST(keychords, keys_in_parallel) {
+ EventHandler ev;
+ EXPECT_TRUE(ev.init());
+ TestFrame test_frame(chords, &ev);
+
+ test_frame.SetChords(triple2_chord);
+ test_frame.WaitForChord(triple2_chord);
+ test_frame.ClrChords(triple2_chord);
+ EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))
+ << "expected only " << android::base::Join(triple2_chord, ' ') << " got "
+ << test_frame.Format();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/parser.cpp b/init/parser.cpp
index 4453aaa..ee6ee06 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
+void Parser::ParseData(const std::string& filename, const std::string& data) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@@ -57,7 +57,7 @@
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@@ -81,7 +81,7 @@
end_section();
if (auto result = callback(std::move(args)); !result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@@ -94,16 +94,20 @@
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
- (*parse_errors)++;
+ parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
+ } else {
+ parse_error_count_++;
+ LOG(ERROR) << filename << ": " << state.line
+ << ": Invalid section keyword found";
}
args.clear();
break;
@@ -114,17 +118,17 @@
}
}
-bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
- LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
+ LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents, parse_errors);
+ ParseData(path, *config_contents);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@@ -133,11 +137,11 @@
return true;
}
-bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
+bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
- PLOG(ERROR) << "Could not import directory '" << path << "'";
+ PLOG(INFO) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
@@ -153,7 +157,7 @@
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
- if (!ParseConfigFile(file, parse_errors)) {
+ if (!ParseConfigFile(file)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@@ -161,16 +165,10 @@
}
bool Parser::ParseConfig(const std::string& path) {
- size_t parse_errors;
- return ParseConfig(path, &parse_errors);
-}
-
-bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
- *parse_errors = 0;
if (is_dir(path.c_str())) {
- return ParseConfigDir(path, parse_errors);
+ return ParseConfigDir(path);
}
- return ParseConfigFile(path, parse_errors);
+ return ParseConfigFile(path);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index f6e237f..3501d8c 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,17 +72,19 @@
Parser();
bool ParseConfig(const std::string& path);
- bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ size_t parse_error_count() const { return parse_error_count_; }
+
private:
- void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
- bool ParseConfigFile(const std::string& path, size_t* parse_errors);
- bool ParseConfigDir(const std::string& path, size_t* parse_errors);
+ void ParseData(const std::string& filename, const 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_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
+ size_t parse_error_count_ = 0;
};
} // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 99d3c83..d1c427d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -56,15 +56,16 @@
#include <selinux/label.h>
#include <selinux/selinux.h>
+#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
+#include "selinux.h"
#include "subcontext.h"
#include "util.h"
using namespace std::literals;
-using android::base::GetIntProperty;
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -94,6 +95,11 @@
void CreateSerializedPropertyInfo();
+struct PropertyAuditData {
+ const ucred* cr;
+ const char* name;
+};
+
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
@@ -110,7 +116,7 @@
return false;
}
- property_audit_data audit_data;
+ PropertyAuditData audit_data;
audit_data.name = name.c_str();
audit_data.cr = &cr;
@@ -371,6 +377,7 @@
int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
if (result <= 0) {
+ PLOG(ERROR) << "sys_prop: recv error";
return false;
}
@@ -378,6 +385,10 @@
data += result;
}
+ if (bytes_left != 0) {
+ LOG(ERROR) << "sys_prop: recv data is not properly obtained.";
+ }
+
return bytes_left == 0;
}
@@ -387,6 +398,35 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
};
+bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr) {
+ // We check the legacy method first but these properties are dontaudit, so we only log an audit
+ // if the newer method fails as well. We only do this with the legacy ctl. properties.
+ if (name == "ctl.start" || name == "ctl.stop" || name == "ctl.restart") {
+ // The legacy permissions model is that ctl. properties have their name ctl.<action> and
+ // their value is the name of the service to apply that action to. Permissions for these
+ // actions are based on the service, so we must create a fake name of ctl.<service> to
+ // check permissions.
+ auto control_string_legacy = "ctl." + value;
+ const char* target_context_legacy = nullptr;
+ const char* type_legacy = nullptr;
+ property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,
+ &type_legacy);
+
+ if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {
+ return true;
+ }
+ }
+
+ auto control_string_full = name + "$" + value;
+ const char* target_context_full = nullptr;
+ const char* type_full = nullptr;
+ property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,
+ &type_full);
+
+ return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);
+}
+
// 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) {
@@ -396,15 +436,9 @@
}
if (StartsWith(name, "ctl.")) {
- // ctl. properties have their name ctl.<action> and their value is the name of the service
- // to apply that action to. Permissions for these actions are based on the service, so we
- // must create a fake name of ctl.<service> to check permissions.
- auto control_string = "ctl." + value;
- const char* target_context = nullptr;
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
- if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
+ if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
+ *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
+ value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
@@ -542,7 +576,7 @@
size_t flen = 0;
const char* context = kInitContext.c_str();
- if (GetIntProperty("ro.vndk.version", 28) >= 28) {
+ if (SelinuxHasVendorInit()) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
if (StartsWith(filename, path_prefix)) {
context = secontext;
@@ -736,7 +770,7 @@
}
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
- property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
+ auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
@@ -808,7 +842,7 @@
selinux_android_restorecon(kPropertyInfosPath, 0);
}
-void start_property_service() {
+void StartPropertyService(Epoll* epoll) {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
@@ -823,7 +857,9 @@
listen(property_set_fd, 8);
- register_epoll_handler(property_set_fd, handle_property_set_fd);
+ if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+ PLOG(FATAL) << result.error();
+ }
}
} // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 29eaaa9..cacd987 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -21,14 +21,11 @@
#include <string>
+#include "epoll.h"
+
namespace android {
namespace init {
-struct property_audit_data {
- const ucred* cr;
- const char* 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,
@@ -40,7 +37,7 @@
void property_load_boot_defaults(void);
void load_persist_props(void);
void load_system_props(void);
-void start_property_service(void);
+void StartPropertyService(Epoll* epoll);
} // namespace init
} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 6aba9c1..0ba5c4a 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -55,12 +55,14 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <selinux/android.h>
#include "log.h"
#include "util.h"
+using android::base::ParseInt;
using android::base::Timer;
using android::base::unique_fd;
@@ -453,6 +455,31 @@
selinux_set_callback(SELINUX_CB_LOG, cb);
}
+// This function checks whether the sepolicy supports vendor init.
+bool SelinuxHasVendorInit() {
+ if (!IsSplitPolicyDevice()) {
+ // If this device does not split sepolicy files, vendor_init will be available in the latest
+ // monolithic sepolicy file.
+ return true;
+ }
+
+ std::string version;
+ if (!GetVendorMappingVersion(&version)) {
+ // Return true as the default if we failed to load the vendor sepolicy version.
+ return true;
+ }
+
+ int major_version;
+ std::string major_version_str(version, 0, version.find('.'));
+ if (!ParseInt(major_version_str, &major_version)) {
+ PLOG(ERROR) << "Failed to parse the vendor sepolicy major version " << major_version_str;
+ // Return true as the default if we failed to parse the major version.
+ return true;
+ }
+
+ return major_version >= 28;
+}
+
// 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
diff --git a/init/selinux.h b/init/selinux.h
index 7b880ec..30069b5 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -27,6 +27,7 @@
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
+bool SelinuxHasVendorInit();
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
diff --git a/init/service.cpp b/init/service.cpp
index 03c2cee..565cae7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/input.h>
#include <linux/securebits.h>
#include <sched.h>
#include <sys/mount.h>
@@ -32,8 +33,10 @@
#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 <processgroup/processgroup.h>
#include <selinux/selinux.h>
@@ -45,8 +48,6 @@
#if defined(__ANDROID__)
#include <sys/system_properties.h>
-#include <android-base/properties.h>
-
#include "init.h"
#include "property_service.h"
#else
@@ -59,13 +60,13 @@
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::unique_fd;
using android::base::WriteStringToFile;
namespace android {
namespace init {
-static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
- const std::string& service_path) {
+static Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
@@ -101,36 +102,49 @@
return computed_context;
}
-static void SetUpPidNamespace(const std::string& service_name) {
+Result<Success> Service::SetUpMountNamespace() const {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // It's OK to LOG(FATAL) in this function since it's running in the first
- // child process.
-
// 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) {
- PLOG(FATAL) << "couldn't remount(/) recursively as slave for " << service_name;
- }
- // umount() then mount() /proc.
- // Note that it is not sufficient to mount with MS_REMOUNT.
- if (umount("/proc") == -1) {
- PLOG(FATAL) << "couldn't umount(/proc) for " << service_name;
- }
- if (mount("", "/proc", "proc", kSafeFlags, "") == -1) {
- PLOG(FATAL) << "couldn't mount(/proc) for " << service_name;
+ return ErrnoError() << "Could not remount(/) recursively as slave";
}
- if (prctl(PR_SET_NAME, service_name.c_str()) == -1) {
- PLOG(FATAL) << "couldn't set name for " << service_name;
+ // 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", "sys", 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) {
- PLOG(FATAL) << "couldn't fork init inside the PID namespace for " << service_name;
+ return ErrnoError() << "Could not fork init inside the PID namespace";
}
if (child_pid > 0) {
@@ -153,6 +167,20 @@
}
_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) {
@@ -200,7 +228,6 @@
seclabel_(seclabel),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
- keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -422,6 +449,20 @@
return Success();
}
+Result<Success> Service::ParseEnterNamespace(const 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, args[2]);
+ return Success();
+}
+
Result<Success> Service::ParseGroup(const std::vector<std::string>& args) {
auto gid = DecodeUid(args[1]);
if (!gid) {
@@ -502,10 +543,13 @@
Result<Success> Service::ParseKeycodes(const std::vector<std::string>& args) {
for (std::size_t i = 1; i < args.size(); i++) {
int code;
- if (ParseInt(args[i], &code)) {
- keycodes_.emplace_back(code);
+ if (ParseInt(args[i], &code, 0, KEY_MAX)) {
+ for (auto& key : keycodes_) {
+ if (key == code) return Error() << "duplicate keycode: " << args[i];
+ }
+ keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
} else {
- LOG(WARNING) << "ignoring invalid keycode: " << args[i];
+ return Error() << "invalid keycode: " << args[i];
}
}
return Success();
@@ -691,6 +735,8 @@
{"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}},
@@ -793,7 +839,7 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- auto result = ComputeContextFromExecutable(name_, args_[0]);
+ auto result = ComputeContextFromExecutable(args_[0]);
if (!result) {
return result.error();
}
@@ -812,10 +858,24 @@
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.
- SetUpPidNamespace(name_);
+ if (auto result = SetUpPidNamespace(); !result) {
+ LOG(FATAL) << "Service '" << name_
+ << "' could not set up PID namespace: " << result.error();
+ }
}
for (const auto& [key, value] : environment_vars_) {
diff --git a/init/service.h b/init/service.h
index cf38f69..ea79a07 100644
--- a/init/service.h
+++ b/init/service.h
@@ -108,8 +108,6 @@
const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
const std::string& seclabel() const { return seclabel_; }
const std::vector<int>& keycodes() const { return keycodes_; }
- int keychord_id() const { return keychord_id_; }
- void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
IoSchedClass ioprio_class() const { return ioprio_class_; }
int ioprio_pri() const { return ioprio_pri_; }
const std::set<std::string>& interfaces() const { return interfaces_; }
@@ -125,6 +123,9 @@
using OptionParser = Result<Success> (Service::*)(const 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;
@@ -137,6 +138,7 @@
Result<Success> ParseConsole(const std::vector<std::string>& args);
Result<Success> ParseCritical(const std::vector<std::string>& args);
Result<Success> ParseDisabled(const std::vector<std::string>& args);
+ Result<Success> ParseEnterNamespace(const std::vector<std::string>& args);
Result<Success> ParseGroup(const std::vector<std::string>& args);
Result<Success> ParsePriority(const std::vector<std::string>& args);
Result<Success> ParseInterface(const std::vector<std::string>& args);
@@ -181,6 +183,8 @@
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::string seclabel_;
@@ -193,9 +197,8 @@
std::set<std::string> interfaces_; // e.g. some.package.foo@1.0::IBaz/instance-name
- // keycodes for triggering this service via /dev/keychord
+ // keycodes for triggering this service via /dev/input/input*
std::vector<int> keycodes_;
- int keychord_id_;
IoSchedClass ioprio_class_;
int ioprio_pri_;
@@ -244,6 +247,16 @@
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(); }
diff --git a/init/service_test.cpp b/init/service_test.cpp
index b43c2e9..194aa2b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -46,7 +46,6 @@
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->keychord_id());
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());
@@ -66,7 +65,6 @@
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->keychord_id());
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());
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 9c0c0bb..267d530 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,8 +30,6 @@
#include "util.h"
#if defined(__ANDROID__)
-#include <android-base/properties.h>
-
#include "property_service.h"
#include "selinux.h"
#else
@@ -39,7 +37,6 @@
#endif
using android::base::GetExecutablePath;
-using android::base::GetIntProperty;
using android::base::Join;
using android::base::Socketpair;
using android::base::Split;
@@ -357,7 +354,7 @@
static std::vector<Subcontext> subcontexts;
std::vector<Subcontext>* InitializeSubcontexts() {
- if (GetIntProperty("ro.vndk.version", 28) >= 28) {
+ if (SelinuxHasVendorInit()) {
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
subcontexts.emplace_back(path_prefix, secontext);
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 1435d82..a284203 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -30,6 +30,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <fstab/fstab.h>
#include <selinux/android.h>
#include <selinux/selinux.h>
@@ -242,8 +243,9 @@
std::string hardware = android::base::GetProperty("ro.hardware", "");
parser.ParseConfig("/ueventd." + hardware + ".rc");
+ auto boot_devices = fs_mgr_get_boot_devices();
return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),
- std::move(subsystems), true);
+ std::move(subsystems), std::move(boot_devices), true);
}
int ueventd_main(int argc, char** argv) {
diff --git a/init/util.cpp b/init/util.cpp
index 4455b2e..5f2b87d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,6 +33,7 @@
#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>
@@ -43,8 +44,6 @@
#include "reboot.h"
#if defined(__ANDROID__)
-#include <android-base/properties.h>
-
#include "selinux.h"
#else
#include "host_init_stubs.h"
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 8a2afea..4ab439d 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -27,6 +27,7 @@
name: "libasyncio",
defaults: ["libasyncio_defaults"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
srcs: [
"AsyncIO.cpp",
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 11b8144..b4bf35f 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -51,12 +51,14 @@
cc_library_headers {
name: "libbacktrace_headers",
vendor_available: true,
+ recovery_available: true,
export_include_dirs: ["include"],
}
cc_library {
name: "libbacktrace",
vendor_available: false,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -91,15 +93,16 @@
"libdexfile",
],
- static_libs: ["libcutils"],
+ static_libs: [
+ "libcutils",
+ "libprocinfo",
+ ],
// libdexfile will eventually properly export headers, for now
// include these directly.
include_dirs: [
"art/runtime",
],
-
- header_libs: ["jni_headers"],
},
android: {
static_libs: ["libasync_safe"],
@@ -108,6 +111,10 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
whole_static_libs: ["libdemangle"],
}
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index bdae140..399721d 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -28,6 +28,9 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
#include <backtrace/backtrace_constants.h>
+#if defined(__linux__)
+#include <procinfo/process_map.h>
+#endif
#include "thread_utils.h"
@@ -60,27 +63,19 @@
*map = {};
}
-bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
+#if defined(__APPLE__)
+static bool ParseLine(const char* line, backtrace_map_t* map) {
uint64_t start;
uint64_t end;
char permissions[5];
int name_pos;
-#if defined(__APPLE__)
// 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) {
-#else
-// Linux /proc/<pid>/maps lines:
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0 1 2 3 4 5
- if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4s %*x %*x:%*x %*d %n",
- &start, &end, permissions, &name_pos) != 3) {
-#endif
return false;
}
@@ -107,24 +102,15 @@
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];
-#else
- char path[sizeof(pid_t)*3 + sizeof("/proc//maps") + 1];
-#endif
char line[1024];
-
-#if defined(__APPLE__)
// cmd is guaranteed to always be big enough to hold this string.
snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid_);
FILE* fp = popen(cmd, "r");
-#else
- // path is guaranteed to always be big enough to hold this string.
- snprintf(path, sizeof(path), "/proc/%d/maps", pid_);
- FILE* fp = fopen(path, "r");
-#endif
if (fp == nullptr) {
return false;
}
@@ -135,13 +121,19 @@
maps_.push_back(map);
}
}
-#if defined(__APPLE__)
pclose(fp);
-#else
- fclose(fp);
-#endif
-
return true;
+#else
+ return android::procinfo::ReadProcessMaps(
+ pid_, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t, const char* name) {
+ maps_.resize(maps_.size() + 1);
+ backtrace_map_t& map = maps_.back();
+ map.start = start;
+ map.end = end;
+ map.flags = flags;
+ map.name = name;
+ });
+#endif
}
#if defined(__APPLE__)
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 1e3d379..f78a31f 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -46,6 +46,7 @@
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -1186,49 +1187,45 @@
ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
}
-static const char* CopySharedLibrary() {
-#if defined(__LP64__)
- const char* lib_name = "lib64";
-#else
- const char* lib_name = "lib";
-#endif
+static void CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+ std::string system_dir;
#if defined(__BIONIC__)
- const char* tmp_so_name = "/data/local/tmp/libbacktrace_test.so";
- std::string cp_cmd = android::base::StringPrintf("cp /system/%s/libbacktrace_test.so %s",
- lib_name, tmp_so_name);
+ system_dir = "/system/lib";
#else
- const char* tmp_so_name = "/tmp/libbacktrace_test.so";
- if (getenv("ANDROID_HOST_OUT") == NULL) {
- fprintf(stderr, "ANDROID_HOST_OUT not set, make sure you run lunch.");
- return nullptr;
- }
- std::string cp_cmd = android::base::StringPrintf("cp %s/%s/libbacktrace_test.so %s",
- getenv("ANDROID_HOST_OUT"), lib_name,
- tmp_so_name);
+ const char* host_out_env = getenv("ANDROID_HOST_OUT");
+ ASSERT_TRUE(host_out_env != nullptr);
+ system_dir = std::string(host_out_env) + "/lib";
#endif
- // Copy the shared so to a tempory directory.
- system(cp_cmd.c_str());
+#if defined(__LP64__)
+ system_dir += "64";
+#endif
- return tmp_so_name;
+ *tmp_so_name = std::string(tmp_dir) + "/libbacktrace_test.so";
+ std::string cp_cmd =
+ android::base::StringPrintf("cp %s/libbacktrace_test.so %s", system_dir.c_str(), tmp_dir);
+
+ // Copy the shared so to a tempory directory.
+ ASSERT_EQ(0, system(cp_cmd.c_str()));
}
TEST(libbacktrace, check_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
std::vector<std::string> found_functions;
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
@@ -1256,32 +1253,33 @@
}
TEST(libbacktrace, check_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
g_ready = 0;
struct stat buf;
- ASSERT_TRUE(stat(tmp_so_name, &buf) != -1);
+ ASSERT_TRUE(stat(tmp_so_name.c_str(), &buf) != -1);
uint64_t map_size = buf.st_size;
pid_t pid;
if ((pid = fork()) == 0) {
- int fd = open(tmp_so_name, O_RDONLY);
+ int fd = open(tmp_so_name.c_str(), O_RDONLY);
if (fd == -1) {
- fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name, strerror(errno));
- unlink(tmp_so_name);
+ fprintf(stderr, "Failed to open file %s: %s\n", tmp_so_name.c_str(), strerror(errno));
+ unlink(tmp_so_name.c_str());
exit(0);
}
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
- unlink(tmp_so_name);
+ unlink(tmp_so_name.c_str());
exit(0);
}
close(fd);
- if (unlink(tmp_so_name) == -1) {
+ if (unlink(tmp_so_name.c_str()) == -1) {
fprintf(stderr, "Failed to unlink: %s\n", strerror(errno));
exit(0);
}
@@ -1394,11 +1392,13 @@
typedef int (*test_func_t)(int, int, int, int, void (*)(void*), void*);
TEST(libbacktrace, unwind_through_unreadable_elf_local) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1411,11 +1411,13 @@
}
TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
- const char* tmp_so_name = CopySharedLibrary();
- ASSERT_TRUE(tmp_so_name != nullptr);
- void* lib_handle = dlopen(tmp_so_name, RTLD_NOW);
+ TemporaryDir td;
+ std::string tmp_so_name;
+ ASSERT_NO_FATAL_FAILURE(CopySharedLibrary(td.path, &tmp_so_name));
+
+ void* lib_handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);
ASSERT_TRUE(lib_handle != nullptr);
- ASSERT_TRUE(unlink(tmp_so_name) != -1);
+ ASSERT_TRUE(unlink(tmp_so_name.c_str()) != -1);
test_func_t test_func;
test_func = reinterpret_cast<test_func_t>(dlsym(lib_handle, "test_level_one"));
@@ -1444,7 +1446,8 @@
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
- &frame_num)) {
+ &frame_num) &&
+ frame_num != 0) {
VerifyUnreadableElfFrame(backtrace.get(), reinterpret_cast<uint64_t>(test_func), frame_num);
done = true;
}
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index c94cad1..a9cfce4 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -169,8 +169,6 @@
virtual uint64_t GetLoadBias(size_t /* index */) { return 0; }
- virtual bool ParseLine(const char* line, backtrace_map_t* map);
-
pid_t pid_;
std::deque<backtrace_map_t> maps_;
std::vector<std::string> suffixes_to_ignore_;
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 47de12a..e47560f 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,7 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index dd46750..cdbb65f 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -32,6 +32,7 @@
cc_library_headers {
name: "libcutils_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
target: {
@@ -54,6 +55,7 @@
enabled: true,
support_system_process: true,
},
+ recovery_available: true,
host_supported: true,
srcs: [
"config_utils.cpp",
@@ -160,6 +162,15 @@
misc_undefined: ["integer"],
},
},
+
+ vendor: {
+ exclude_srcs: [
+ // qtaguid.cpp loads libnetd_client.so with dlopen(). Since
+ // the interface of libnetd_client.so may vary between AOSP
+ // releases, exclude qtaguid.cpp from the VNDK-SP variant.
+ "qtaguid.cpp",
+ ],
+ }
},
shared_libs: ["liblog"],
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index b2bec99..bb990d5 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -24,7 +24,6 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/libcutils/canned_fs_config.cpp b/libcutils/canned_fs_config.cpp
index 6b5763b..2772ef0 100644
--- a/libcutils/canned_fs_config.cpp
+++ b/libcutils/canned_fs_config.cpp
@@ -21,7 +21,6 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 0f2b460..6169324 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,6 +80,7 @@
{ 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" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
@@ -195,6 +196,7 @@
{ 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, "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/*" },
@@ -237,9 +239,10 @@
return fd;
}
-// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+// 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[] = {"vendor/", "oem/", "odm/"};
+ static const char* partitions[] = {"odm/", "oem/", "product/", "vendor/"};
for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
size_t plen = strlen(partitions[i]);
if (len <= plen) continue;
diff --git a/libcutils/hashmap.cpp b/libcutils/hashmap.cpp
index 65b6ab1..10e3b25 100644
--- a/libcutils/hashmap.cpp
+++ b/libcutils/hashmap.cpp
@@ -21,7 +21,6 @@
#include <cutils/threads.h>
#include <stdlib.h>
#include <string.h>
-#include <stdbool.h>
#include <sys/types.h>
typedef struct Entry Entry;
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index f95c6c5..c9580af 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -24,7 +24,6 @@
#include <limits.h>
#include <pthread.h>
#include <stdatomic.h>
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index f3593ff..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -2,6 +2,7 @@
name: "libkeyutils",
cflags: ["-Werror"],
defaults: ["linux_bionic_supported"],
+ recovery_available: true,
export_include_dirs: ["include/"],
local_include_dirs: ["include/"],
srcs: ["keyutils.cpp"],
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 7d9e306..4a165a0 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -46,6 +46,7 @@
name: "liblog_headers",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
export_include_dirs: ["include"],
target: {
windows: {
@@ -65,7 +66,7 @@
cc_library {
name: "liblog",
host_supported: true,
-
+ recovery_available: true,
srcs: liblog_sources,
target: {
@@ -82,6 +83,7 @@
},
android_arm: {
// TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
},
windows: {
diff --git a/liblog/logprint.c b/liblog/logprint.c
index a2839bf..7937cb1 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -1632,8 +1632,10 @@
prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "\x1B[38;5;%dm",
colorFromPri(entry->priority));
prefixLen = MIN(prefixLen, sizeof(prefixBuf));
- suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), "\x1B[0m");
- suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+
+ const char suffixContents[] = "\x1B[0m";
+ strcpy(suffixBuf, suffixContents);
+ suffixLen = strlen(suffixContents);
}
char uid[16];
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index f164a19..248a9d2 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -29,7 +29,6 @@
"HeapWalker.cpp",
"LeakFolding.cpp",
"LeakPipe.cpp",
- "LineBuffer.cpp",
"MemUnreachable.cpp",
"ProcessMappings.cpp",
"PtracerThread.cpp",
@@ -38,6 +37,7 @@
static_libs: [
"libc_malloc_debug_backtrace",
+ "libprocinfo",
],
// Only need this for arm since libc++ uses its own unwind code that
// doesn't mix with the other default unwind code.
diff --git a/libmemunreachable/LineBuffer.cpp b/libmemunreachable/LineBuffer.cpp
deleted file mode 100644
index 4ea0542..0000000
--- a/libmemunreachable/LineBuffer.cpp
+++ /dev/null
@@ -1,66 +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.
- */
-
-// Copied from system/extras/memory_replay/LineBuffer.cpp
-// TODO(ccross): find a way to share between libmemunreachable and memory_replay?
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "LineBuffer.h"
-
-namespace android {
-
-LineBuffer::LineBuffer(int fd, char* buffer, size_t buffer_len)
- : fd_(fd), buffer_(buffer), buffer_len_(buffer_len) {}
-
-bool LineBuffer::GetLine(char** line, size_t* line_len) {
- while (true) {
- if (bytes_ > 0) {
- char* newline = reinterpret_cast<char*>(memchr(buffer_ + start_, '\n', bytes_));
- if (newline != nullptr) {
- *newline = '\0';
- *line = buffer_ + start_;
- start_ = newline - buffer_ + 1;
- bytes_ -= newline - *line + 1;
- *line_len = newline - *line;
- return true;
- }
- }
- if (start_ > 0) {
- // Didn't find anything, copy the current to the front of the buffer.
- memmove(buffer_, buffer_ + start_, bytes_);
- start_ = 0;
- }
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd_, buffer_ + bytes_, buffer_len_ - bytes_ - 1));
- if (bytes <= 0) {
- if (bytes_ > 0) {
- // The read data might not contain a nul terminator, so add one.
- buffer_[bytes_] = '\0';
- *line = buffer_ + start_;
- *line_len = bytes_;
- bytes_ = 0;
- start_ = 0;
- return true;
- }
- return false;
- }
- bytes_ += bytes;
- }
-}
-
-} // namespace android
diff --git a/libmemunreachable/LineBuffer.h b/libmemunreachable/LineBuffer.h
deleted file mode 100644
index cc6cd0c..0000000
--- a/libmemunreachable/LineBuffer.h
+++ /dev/null
@@ -1,40 +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 _LIBMEMUNREACHABLE_LINE_BUFFER_H
-#define _LIBMEMUNREACHABLE_LINE_BUFFER_H
-
-#include <stdint.h>
-
-namespace android {
-
-class LineBuffer {
- public:
- LineBuffer(int fd, char* buffer, size_t buffer_len);
-
- bool GetLine(char** line, size_t* line_len);
-
- private:
- int fd_;
- char* buffer_ = nullptr;
- size_t buffer_len_ = 0;
- size_t start_ = 0;
- size_t bytes_ = 0;
-};
-
-} // namespace android
-
-#endif // _LIBMEMUNREACHABLE_LINE_BUFFER_H
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 24fdc7f..529a043 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -495,6 +495,21 @@
return oss.str();
}
+UnreachableMemoryInfo::~UnreachableMemoryInfo() {
+ // Clear the memory that holds the leaks, otherwise the next attempt to
+ // detect leaks may find the old data (for example in the jemalloc tcache)
+ // and consider all the leaks to be referenced.
+ memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
+
+ std::vector<Leak> tmp;
+ leaks.swap(tmp);
+
+ // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+ // there are no copies of the leaked pointer addresses there.
+ malloc_disable();
+ malloc_enable();
+}
+
std::string GetUnreachableMemoryString(bool log_contents, size_t limit) {
UnreachableMemoryInfo info;
if (!GetUnreachableMemory(info, limit)) {
diff --git a/libmemunreachable/ProcessMappings.cpp b/libmemunreachable/ProcessMappings.cpp
index 9a06870..701ce16 100644
--- a/libmemunreachable/ProcessMappings.cpp
+++ b/libmemunreachable/ProcessMappings.cpp
@@ -14,21 +14,30 @@
* limitations under the License.
*/
+#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
-#include "LineBuffer.h"
#include "ProcessMappings.h"
-#include "log.h"
namespace android {
-// This function is not re-entrant since it uses a static buffer for
-// the line data.
+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 {
+ mappings_.emplace_back(start, end, flags & PROT_READ, flags & PROT_WRITE, flags & PROT_EXEC,
+ name);
+ }
+
+ allocator::vector<Mapping>& mappings_;
+};
+
bool ProcessMappings(pid_t pid, allocator::vector<Mapping>& mappings) {
char map_buffer[1024];
snprintf(map_buffer, sizeof(map_buffer), "/proc/%d/maps", pid);
@@ -36,35 +45,13 @@
if (fd == -1) {
return false;
}
-
- LineBuffer line_buf(fd, map_buffer, sizeof(map_buffer));
- char* line;
- size_t line_len;
- while (line_buf.GetLine(&line, &line_len)) {
- int name_pos;
- char perms[5];
- Mapping mapping{};
- if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4s %*x %*x:%*x %*d %n", &mapping.begin,
- &mapping.end, perms, &name_pos) == 3) {
- if (perms[0] == 'r') {
- mapping.read = true;
- }
- if (perms[1] == 'w') {
- mapping.write = true;
- }
- if (perms[2] == 'x') {
- mapping.execute = true;
- }
- if (perms[3] == 'p') {
- mapping.priv = true;
- }
- if ((size_t)name_pos < line_len) {
- strlcpy(mapping.name, line + name_pos, sizeof(mapping.name));
- }
- mappings.emplace_back(mapping);
- }
+ allocator::string content(mappings.get_allocator());
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, map_buffer, sizeof(map_buffer)))) > 0) {
+ content.append(map_buffer, n);
}
- return true;
+ ReadMapCallback callback(mappings);
+ return android::procinfo::ReadMapFileContent(&content[0], callback);
}
} // namespace android
diff --git a/libmemunreachable/ProcessMappings.h b/libmemunreachable/ProcessMappings.h
index a0e97e9..94da69b 100644
--- a/libmemunreachable/ProcessMappings.h
+++ b/libmemunreachable/ProcessMappings.h
@@ -17,6 +17,8 @@
#ifndef LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
#define LIBMEMUNREACHABLE_PROCESS_MAPPING_H_
+#include <string.h>
+
#include "Allocator.h"
namespace android {
@@ -27,8 +29,13 @@
bool read;
bool write;
bool execute;
- bool priv;
char name[96];
+
+ Mapping() {}
+ Mapping(uintptr_t begin, uintptr_t end, bool read, bool write, bool execute, const char* name)
+ : begin(begin), end(end), read(read), write(write), execute(execute) {
+ strlcpy(this->name, name, sizeof(this->name));
+ }
};
// This function is not re-entrant since it uses a static buffer for
diff --git a/libmemunreachable/README.md b/libmemunreachable/README.md
index ae8fa94..9cc0c9b 100644
--- a/libmemunreachable/README.md
+++ b/libmemunreachable/README.md
@@ -12,6 +12,27 @@
Usage
-------
+### In Android apps ###
+
+libmemunreachble is loaded by zygote and can be triggered with `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To enable malloc\_debug backtraces on allocations for a single app process on a userdebug device, use:
+```
+adb root
+adb shell setprop libc.debug.malloc.program app_process
+adb shell setprop wrap.[process] "\$\@"
+adb shell setprop libc.debug.malloc.options backtrace=4
+```
+
+Kill and restart the app, trigger the leak, and then run `dumpsys -t 600 meminfo --unreachable [process]`.
+
+To disable malloc\_debug:
+```
+adb shell setprop libc.debug.malloc.options "''"
+adb shell setprop libc.debug.malloc.program "''"
+adb shell setprop wrap.[process] "''"
+```
+
### C interface ###
#### `bool LogUnreachableMemory(bool log_contents, size_t limit)` ####
@@ -23,7 +44,7 @@
### C++ interface ###
-####`bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)`####
+#### `bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)` ####
Updates an `UnreachableMemoryInfo` object with information on leaks, including details on up to `limit` leaks. Returns true if leak detection succeeded.
#### `std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)` ####
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 438fcaf..c028eab 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -62,12 +62,7 @@
size_t allocation_bytes;
UnreachableMemoryInfo() {}
- ~UnreachableMemoryInfo() {
- // Clear the memory that holds the leaks, otherwise the next attempt to
- // detect leaks may find the old data (for example in the jemalloc tcache)
- // and consider all the leaks to be referenced.
- memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak));
- }
+ ~UnreachableMemoryInfo();
std::string ToString(bool log_contents) const;
};
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 87417f1..bba0c6d 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -23,6 +23,8 @@
#include <memunreachable/memunreachable.h>
+#include "bionic.h"
+
namespace android {
class HiddenPointer {
@@ -48,7 +50,35 @@
write(0, ptr, 0);
}
-TEST(MemunreachableTest, clean) {
+class MemunreachableTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ CleanStack(8192);
+ CleanTcache();
+ }
+
+ virtual void TearDown() {
+ CleanStack(8192);
+ CleanTcache();
+ }
+
+ // Allocate a buffer on the stack and zero it to make sure there are no
+ // stray pointers from old test runs.
+ void __attribute__((noinline)) CleanStack(size_t size) {
+ void* buf = alloca(size);
+ memset(buf, 0, size);
+ Ref(&buf);
+ }
+
+ // Disable and re-enable malloc to flush the jemalloc tcache to make sure
+ // there are stray pointers from old test runs there.
+ void CleanTcache() {
+ malloc_disable();
+ malloc_enable();
+ }
+};
+
+TEST_F(MemunreachableTest, clean) {
UnreachableMemoryInfo info;
ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -57,7 +87,7 @@
ASSERT_EQ(0U, info.leaks.size());
}
-TEST(MemunreachableTest, stack) {
+TEST_F(MemunreachableTest, stack) {
HiddenPointer hidden_ptr;
{
@@ -91,7 +121,7 @@
void* g_ptr;
-TEST(MemunreachableTest, global) {
+TEST_F(MemunreachableTest, global) {
HiddenPointer hidden_ptr;
g_ptr = hidden_ptr.Get();
@@ -122,7 +152,7 @@
}
}
-TEST(MemunreachableTest, tls) {
+TEST_F(MemunreachableTest, tls) {
HiddenPointer hidden_ptr;
pthread_key_t key;
pthread_key_create(&key, nullptr);
@@ -157,10 +187,22 @@
pthread_key_delete(key);
}
-TEST(MemunreachableTest, twice) {
+TEST_F(MemunreachableTest, twice) {
HiddenPointer hidden_ptr;
{
+ void* ptr = hidden_ptr.Get();
+ Ref(&ptr);
+
+ UnreachableMemoryInfo info;
+
+ ASSERT_TRUE(GetUnreachableMemory(info));
+ ASSERT_EQ(0U, info.leaks.size());
+
+ ptr = nullptr;
+ }
+
+ {
UnreachableMemoryInfo info;
ASSERT_TRUE(GetUnreachableMemory(info));
@@ -184,7 +226,7 @@
}
}
-TEST(MemunreachableTest, log) {
+TEST_F(MemunreachableTest, log) {
HiddenPointer hidden_ptr;
ASSERT_TRUE(LogUnreachableMemory(true, 100));
@@ -199,17 +241,23 @@
}
}
-TEST(MemunreachableTest, notdumpable) {
+TEST_F(MemunreachableTest, notdumpable) {
+ if (getuid() == 0) {
+ // TODO(ccross): make this a skipped test when gtest supports them
+ printf("[ SKIP ] Not testable when running as root\n");
+ return;
+ }
+
ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0));
HiddenPointer hidden_ptr;
- ASSERT_TRUE(LogUnreachableMemory(true, 100));
+ EXPECT_FALSE(LogUnreachableMemory(true, 100));
ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1));
}
-TEST(MemunreachableTest, leak_lots) {
+TEST_F(MemunreachableTest, leak_lots) {
std::vector<HiddenPointer> hidden_ptrs;
hidden_ptrs.resize(1024);
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 6549b8d..e6e17ce 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -29,6 +29,13 @@
defaults: ["metricslogger_defaults"],
}
+// static version of libmetricslogger, needed by a few art static binaries
+cc_library_static {
+ name: "libmetricslogger_static",
+ srcs: metricslogger_lib_src_files,
+ defaults: ["metricslogger_defaults"],
+}
+
// metricslogger shared library, debug
// -----------------------------------------------------------------------------
cc_library_shared {
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 189bc4b..2c76869 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <log/log_event_list.h>
#include <cstdint>
#include <string>
@@ -32,6 +33,34 @@
// |value| in the field |field|.
void LogMultiAction(int32_t category, int32_t field, const std::string& value);
+// Logs a Tron complex event.
+//
+// A complex event can include data in a structure not suppored by the other
+// log event types above.
+//
+// Note that instances of this class are single use. You must call Record()
+// to write the event to the event log.
+class ComplexEventLogger {
+ private:
+ android_log_event_list logger;
+
+ public:
+ // Create a complex event with category|category|.
+ explicit ComplexEventLogger(int category);
+ // Add tagged data to the event, with the given tag and integer value.
+ void AddTaggedData(int tag, int32_t value);
+ // Add tagged data to the event, with the given tag and string value.
+ void AddTaggedData(int tag, const std::string& value);
+ // Add tagged data to the event, with the given tag and integer value.
+ void AddTaggedData(int tag, int64_t value);
+ // Add tagged data to the event, with the given tag and float value.
+ void AddTaggedData(int tag, float value);
+ // Record this event. This method can only be used once per instance
+ // of ComplexEventLogger. Do not made any subsequent calls to AddTaggedData
+ // after recording an event.
+ void Record();
+};
+
// TODO: replace these with the metric_logger.proto definitions
enum {
LOGBUILDER_CATEGORY = 757,
@@ -44,11 +73,23 @@
ACTION_BOOT = 1098,
FIELD_PLATFORM_REASON = 1099,
+
+ ACTION_HIDDEN_API_ACCESSED = 1391,
+ FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
+ FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
+ FIELD_HIDDEN_API_SIGNATURE = 1394,
};
enum {
TYPE_ACTION = 4,
};
+enum {
+ ACCESS_METHOD_NONE = 0,
+ ACCESS_METHOD_REFLECTION = 1,
+ ACCESS_METHOD_JNI = 2,
+ ACCESS_METHOD_LINKING = 3,
+};
+
} // namespace metricslogger
} // namespace android
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index fdc4407..912fa12 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -23,9 +23,14 @@
namespace {
+#ifdef __ANDROID__
EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
const int kSysuiMultiActionTag = android_lookupEventTagNum(
kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
+#else
+// android_openEventTagMap does not work on host builds.
+const int kSysuiMultiActionTag = 0;
+#endif
} // namespace
@@ -53,5 +58,29 @@
<< field << value << LOG_ID_EVENTS;
}
+ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionTag) {
+ logger << LOGBUILDER_CATEGORY << category;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, const std::string& value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, int64_t value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::AddTaggedData(int tag, float value) {
+ logger << tag << value;
+}
+
+void ComplexEventLogger::Record() {
+ logger << LOG_ID_EVENTS;
+}
+
} // namespace metricslogger
} // namespace android
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f2cc942..5c28a9f 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1 +1,2 @@
dimitry@google.com
+jiyong@google.com
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 0ebb226..7fef106 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -46,6 +46,8 @@
"%s:%d: %s CHECK '" #predicate "' failed.",\
__FILE__, __LINE__, __FUNCTION__)
+using namespace std::string_literals;
+
namespace android {
#if defined(__ANDROID__)
@@ -236,10 +238,15 @@
// 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 if (!oem_public_libraries_.empty()) {
- // oem_public_libraries are NOT available to vendor apks, otherwise it
+ } else {
+ // oem and product public libraries are NOT available to vendor apks, otherwise it
// would be system->vendor violation.
- system_exposed_libraries = system_exposed_libraries + ":" + oem_public_libraries_.c_str();
+ 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;
@@ -351,6 +358,8 @@
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),
@@ -373,7 +382,7 @@
//
// 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
+ // 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) {
@@ -387,48 +396,15 @@
// system libs that are exposed to apps. The libs in the txt files must be
// named as lib<name>.<companyname>.so.
sonames.clear();
- std::string dirname = base::Dirname(public_native_libraries_system_config);
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
- if (dir != nullptr) {
- // Failing to opening the dir is not an error, which can happen in
- // webview_zygote.
- struct dirent* ent;
- while ((ent = readdir(dir.get())) != nullptr) {
- 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 + "/" + 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());
- 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());
- }
- }
- }
+ 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);
@@ -448,11 +424,54 @@
vendor_public_libraries_ = base::Join(sonames, ':');
}
- void Reset() {
- namespaces_.clear();
- }
+ 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,
@@ -559,6 +578,7 @@
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_;
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index 5cf88b0..d528f30 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -49,3 +49,23 @@
"libbase",
],
}
+
+cc_library {
+ name: "libfoo.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libbar.product1",
+ srcs: ["test.cpp"],
+ cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+ product_specific: true,
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
index e625454..65e7b09 100644
--- a/libnativeloader/test/Android.mk
+++ b/libnativeloader/test/Android.mk
@@ -30,6 +30,13 @@
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-product1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := oemlibrarytest-system
LOCAL_MODULE_TAGS := tests
LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
index 214892d..a7a455d 100644
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -29,6 +29,8 @@
tryLoadingLib("bar.oem1");
tryLoadingLib("foo.oem2");
tryLoadingLib("bar.oem2");
+ tryLoadingLib("foo.product1");
+ tryLoadingLib("bar.product1");
}
private void tryLoadingLib(String name) {
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 27693b3..c38594a 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libpackagelistparser",
+ recovery_available: true,
srcs: ["packagelistparser.c"],
cflags: [
"-Wall",
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 6dfa697..1cebb5d 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -39,12 +39,14 @@
#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 <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+using android::base::GetBoolProperty;
using android::base::StartsWith;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -62,12 +64,20 @@
static const std::string& GetCgroupRootPath() {
static std::string cgroup_root_path;
std::call_once(init_path_flag, [&]() {
- // Check if mem cgroup is mounted, only then check for write-access to avoid
- // SELinux denials
+ // 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);
- });
+ (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
+ ACCT_CGROUP_PATH : MEM_CGROUP_PATH);
+ } else {
+ cgroup_root_path = ACCT_CGROUP_PATH;
+ }
+ });
return cgroup_root_path;
}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index b35882c..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -27,6 +27,7 @@
name: "libprocinfo",
defaults: ["libprocinfo_defaults"],
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
},
@@ -59,6 +60,7 @@
host_supported: true,
srcs: [
"process_test.cpp",
+ "process_map_test.cpp",
],
target: {
darwin: {
@@ -84,5 +86,36 @@
},
},
+ data: [
+ "testdata/*",
+ ],
+
test_suites: ["device-tests"],
}
+
+cc_benchmark {
+ name: "libprocinfo_benchmark",
+ defaults: ["libprocinfo_defaults"],
+ srcs: [
+ "process_map_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ "libprocinfo",
+ "libunwindstack",
+ ],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ data: [
+ "testdata/*",
+ ],
+}
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
new file mode 100644
index 0000000..3771f9f
--- /dev/null
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -0,0 +1,151 @@
+/*
+ * 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 <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+
+#include <android-base/file.h>
+
+namespace android {
+namespace procinfo {
+
+template <class CallbackType>
+bool ReadMapFileContent(char* content, const CallbackType& callback) {
+ uint64_t start_addr;
+ uint64_t end_addr;
+ uint16_t flags;
+ uint64_t pgoff;
+ char* next_line = content;
+ char* p;
+
+ auto pass_space = [&]() {
+ if (*p != ' ') {
+ return false;
+ }
+ while (*p == ' ') {
+ p++;
+ }
+ return true;
+ };
+
+ auto pass_xdigit = [&]() {
+ if (!isxdigit(*p)) {
+ return false;
+ }
+ do {
+ p++;
+ } while (isxdigit(*p));
+ return true;
+ };
+
+ while (next_line != nullptr && *next_line != '\0') {
+ p = next_line;
+ next_line = strchr(next_line, '\n');
+ if (next_line != nullptr) {
+ *next_line = '\0';
+ next_line++;
+ }
+ // Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http
+ char* end;
+ // start_addr
+ start_addr = strtoull(p, &end, 16);
+ if (end == p || *end != '-') {
+ return false;
+ }
+ p = end + 1;
+ // end_addr
+ end_addr = strtoull(p, &end, 16);
+ if (end == p) {
+ return false;
+ }
+ p = end;
+ if (!pass_space()) {
+ return false;
+ }
+ // flags
+ flags = 0;
+ if (*p == 'r') {
+ flags |= PROT_READ;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p == 'w') {
+ flags |= PROT_WRITE;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p == 'x') {
+ flags |= PROT_EXEC;
+ } else if (*p != '-') {
+ return false;
+ }
+ p++;
+ if (*p != 'p' && *p != 's') {
+ return false;
+ }
+ p++;
+ if (!pass_space()) {
+ return false;
+ }
+ // pgoff
+ pgoff = strtoull(p, &end, 16);
+ if (end == p) {
+ return false;
+ }
+ p = end;
+ if (!pass_space()) {
+ return false;
+ }
+ // major:minor
+ if (!pass_xdigit() || *p++ != ':' || !pass_xdigit() || !pass_space()) {
+ return false;
+ }
+ // inode
+ if (!pass_xdigit() || (*p != '\0' && !pass_space())) {
+ return false;
+ }
+ // filename
+ callback(start_addr, end_addr, flags, pgoff, 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) {
+ std::string content;
+ if (!android::base::ReadFileToString(map_file, &content)) {
+ return false;
+ }
+ 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) {
+ return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_benchmark.cpp b/libprocinfo/process_map_benchmark.cpp
new file mode 100644
index 0000000..d9e8a4d
--- /dev/null
+++ b/libprocinfo/process_map_benchmark.cpp
@@ -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.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <string.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <backtrace/BacktraceMap.h>
+#include <unwindstack/Maps.h>
+
+#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); });
+ CHECK_EQ(maps.size(), 2043u);
+ }
+}
+BENCHMARK(BM_ReadMapFile);
+
+static void BM_unwindstack_FileMaps(benchmark::State& state) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ for (auto _ : state) {
+ unwindstack::FileMaps maps(map_file);
+ maps.Parse();
+ CHECK_EQ(maps.Total(), 2043u);
+ }
+}
+BENCHMARK(BM_unwindstack_FileMaps);
+
+static void BM_unwindstack_BufferMaps(benchmark::State& state) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ std::string content;
+ CHECK(android::base::ReadFileToString(map_file, &content));
+ for (auto _ : state) {
+ unwindstack::BufferMaps maps(content.c_str());
+ maps.Parse();
+ CHECK_EQ(maps.Total(), 2043u);
+ }
+}
+BENCHMARK(BM_unwindstack_BufferMaps);
+
+static void BM_backtrace_BacktraceMap(benchmark::State& state) {
+ pid_t pid = getpid();
+ for (auto _ : state) {
+ BacktraceMap* map = BacktraceMap::Create(pid, true);
+ CHECK(map != nullptr);
+ delete map;
+ }
+}
+BENCHMARK(BM_backtrace_BacktraceMap);
+
+BENCHMARK_MAIN();
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
new file mode 100644
index 0000000..900fd85
--- /dev/null
+++ b/libprocinfo/process_map_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 <procinfo/process_map.h>
+
+#include <string>
+
+#include <android-base/file.h>
+
+#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) {
+ std::string map_file = android::base::GetExecutableDirectory() + "/testdata/maps";
+ std::vector<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); }));
+ 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].name, "/dev/ashmem/dalvik-main space (region space) (deleted)");
+ 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].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].name,
+ "/dev/ashmem/dalvik-classes.dex extracted in memory from "
+ "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)");
+}
diff --git a/libprocinfo/testdata/maps b/libprocinfo/testdata/maps
new file mode 100644
index 0000000..3b312e3
--- /dev/null
+++ b/libprocinfo/testdata/maps
@@ -0,0 +1,2043 @@
+12c00000-2ac00000 rw-p 00000000 00:05 10267643 /dev/ashmem/dalvik-main space (region space) (deleted)
+6fb5d000-6fd6e000 rw-p 00000000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+6fd6e000-6fd82000 r--p 00211000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+6fd82000-6fe47000 rw-p 00000000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe47000-6fe52000 r--p 000c5000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+6fe52000-6fe84000 rw-p 00000000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe84000-6fe87000 r--p 00032000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+6fe87000-6feb2000 rw-p 00000000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb2000-6feb5000 r--p 0002b000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+6feb5000-6fef4000 rw-p 00000000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fef4000-6fefb000 r--p 0003f000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+6fefb000-6ff3f000 rw-p 00000000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff3f000-6ff45000 r--p 00044000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+6ff45000-6ff7a000 rw-p 00000000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff7a000-6ff85000 r--p 00035000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+6ff85000-70594000 rw-p 00000000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70594000-705cb000 r--p 0060f000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+705cb000-7061f000 rw-p 00000000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+7061f000-70629000 r--p 00054000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70629000-70635000 rw-p 00000000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70635000-70636000 r--p 0000c000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70636000-70644000 rw-p 00000000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70644000-70645000 r--p 0000e000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70645000-70648000 rw-p 00000000 103:1d 639544 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70648000-7064c000 rw-p 00000000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064c000-7064d000 r--p 00004000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7064d000-7064e000 rw-p 00000000 103:1d 639550 /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+7064e000-70652000 rw-p 00000000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70652000-70653000 r--p 00004000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70653000-70654000 rw-p 00000000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70654000-70655000 r--p 00001000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70655000-70731000 r--p 00000000 fc:00 940 /system/framework/arm64/boot.oat
+70731000-709ca000 r-xp 000dc000 fc:00 940 /system/framework/arm64/boot.oat
+709ca000-709cb000 rw-p 00000000 00:00 0 [anon:.bss]
+709cb000-70e4c000 r--s 00000000 fc:00 961 /system/framework/boot.vdex
+70e4c000-70e4d000 r--p 00375000 fc:00 940 /system/framework/arm64/boot.oat
+70e4d000-70e4e000 rw-p 00376000 fc:00 940 /system/framework/arm64/boot.oat
+70e4e000-70eab000 r--p 00000000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+70eab000-70fad000 r-xp 0005d000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+70fad000-70fae000 rw-p 00000000 00:00 0 [anon:.bss]
+70fae000-712a9000 r--s 00000000 fc:00 702 /system/framework/boot-core-libart.vdex
+712a9000-712aa000 r--p 0015f000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+712aa000-712ab000 rw-p 00160000 fc:00 916 /system/framework/arm64/boot-core-libart.oat
+712ab000-712bb000 r--p 00000000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+712bb000-712e4000 r-xp 00010000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+712e4000-712e5000 rw-p 00000000 00:00 0 [anon:.bss]
+712e5000-71346000 r--s 00000000 fc:00 970 /system/framework/boot-conscrypt.vdex
+71346000-71347000 r--p 00039000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+71347000-71348000 rw-p 0003a000 fc:00 941 /system/framework/arm64/boot-conscrypt.oat
+71348000-71361000 r--p 00000000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71361000-713a3000 r-xp 00019000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+713a3000-713a4000 rw-p 00000000 00:00 0 [anon:.bss]
+713a4000-71403000 r--s 00000000 fc:00 886 /system/framework/boot-okhttp.vdex
+71403000-71404000 r--p 0005b000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71404000-71405000 rw-p 0005c000 fc:00 908 /system/framework/arm64/boot-okhttp.oat
+71405000-71415000 r--p 00000000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+71415000-71437000 r-xp 00010000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+71437000-71438000 rw-p 00000000 00:00 0 [anon:.bss]
+71438000-7157b000 r--s 00000000 fc:00 1006 /system/framework/boot-bouncycastle.vdex
+7157b000-7157c000 r--p 00032000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+7157c000-7157d000 rw-p 00033000 fc:00 936 /system/framework/arm64/boot-bouncycastle.oat
+7157d000-71583000 r--p 00000000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+71583000-71584000 r-xp 00006000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+71584000-716a7000 r--s 00000000 fc:00 883 /system/framework/boot-apache-xml.vdex
+716a7000-716a8000 r--p 00007000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+716a8000-716a9000 rw-p 00008000 fc:00 932 /system/framework/arm64/boot-apache-xml.oat
+716a9000-716b5000 r--p 00000000 fc:00 891 /system/framework/arm64/boot-ext.oat
+716b5000-716cc000 r-xp 0000c000 fc:00 891 /system/framework/arm64/boot-ext.oat
+716cc000-716cd000 rw-p 00000000 00:00 0 [anon:.bss]
+716cd000-717b8000 r--s 00000000 fc:00 879 /system/framework/boot-ext.vdex
+717b8000-717b9000 r--p 00023000 fc:00 891 /system/framework/arm64/boot-ext.oat
+717b9000-717ba000 rw-p 00024000 fc:00 891 /system/framework/arm64/boot-ext.oat
+717ba000-71aeb000 r--p 00000000 fc:00 943 /system/framework/arm64/boot-framework.oat
+71aeb000-72390000 r-xp 00331000 fc:00 943 /system/framework/arm64/boot-framework.oat
+72390000-72396000 rw-p 00000000 00:00 0 [anon:.bss]
+72396000-73746000 r--s 00000000 fc:00 985 /system/framework/boot-framework.vdex
+73746000-73747000 r--p 00bd6000 fc:00 943 /system/framework/arm64/boot-framework.oat
+73747000-73748000 rw-p 00bd7000 fc:00 943 /system/framework/arm64/boot-framework.oat
+73748000-73780000 r--p 00000000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73780000-73818000 r-xp 00038000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73818000-7381a000 rw-p 00000000 00:00 0 [anon:.bss]
+7381a000-73af0000 r--s 00000000 fc:00 697 /system/framework/boot-telephony-common.vdex
+73af0000-73af1000 r--p 000d0000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73af1000-73af2000 rw-p 000d1000 fc:00 893 /system/framework/arm64/boot-telephony-common.oat
+73af2000-73af6000 r--p 00000000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73af6000-73af8000 r-xp 00004000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73af8000-73af9000 rw-p 00000000 00:00 0 [anon:.bss]
+73af9000-73b1e000 r--s 00000000 fc:00 959 /system/framework/boot-voip-common.vdex
+73b1e000-73b1f000 r--p 00006000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73b1f000-73b20000 rw-p 00007000 fc:00 922 /system/framework/arm64/boot-voip-common.oat
+73b20000-73b23000 r--p 00000000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b23000-73b25000 r-xp 00003000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b25000-73b26000 rw-p 00000000 00:00 0 [anon:.bss]
+73b26000-73b48000 r--s 00000000 fc:00 957 /system/framework/boot-ims-common.vdex
+73b48000-73b49000 r--p 00005000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b49000-73b4a000 rw-p 00006000 fc:00 918 /system/framework/arm64/boot-ims-common.oat
+73b4a000-73b4d000 r--p 00000000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4d000-73b4e000 r-xp 00003000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b4e000-73b55000 r--s 00000000 fc:00 972 /system/framework/boot-android.hidl.base-V1.0-java.vdex
+73b55000-73b56000 r--p 00004000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b56000-73b57000 rw-p 00005000 fc:00 909 /system/framework/arm64/boot-android.hidl.base-V1.0-java.oat
+73b57000-73b5a000 r--p 00000000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5a000-73b5c000 r-xp 00003000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b5c000-73b5d000 rw-p 00000000 00:00 0 [anon:.bss]
+73b5d000-73b68000 r--s 00000000 fc:00 704 /system/framework/boot-android.hidl.manager-V1.0-java.vdex
+73b68000-73b69000 r--p 00005000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b69000-73b6a000 rw-p 00006000 fc:00 954 /system/framework/arm64/boot-android.hidl.manager-V1.0-java.oat
+73b6a000-73b6d000 r--p 00000000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6d000-73b6e000 r-xp 00003000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b6e000-73b6f000 r--s 00000000 fc:00 994 /system/framework/boot-framework-oahl-backward-compatibility.vdex
+73b6f000-73b70000 r--p 00004000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b70000-73b71000 rw-p 00005000 fc:00 904 /system/framework/arm64/boot-framework-oahl-backward-compatibility.oat
+73b71000-73b75000 r--p 00000000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b75000-73b79000 r-xp 00004000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b79000-73b7a000 rw-p 00000000 00:00 0 [anon:.bss]
+73b7a000-73b82000 r--s 00000000 fc:00 706 /system/framework/boot-android.test.base.vdex
+73b82000-73b83000 r--p 00008000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b83000-73b84000 rw-p 00009000 fc:00 896 /system/framework/arm64/boot-android.test.base.oat
+73b84000-73b87000 r--p 00000000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b87000-73b88000 r-xp 00003000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b88000-73b89000 r--s 00000000 fc:00 884 /system/framework/boot-com.google.vr.platform.vdex
+73b89000-73b8a000 r--p 00004000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8a000-73b8b000 rw-p 00005000 fc:00 899 /system/framework/arm64/boot-com.google.vr.platform.oat
+73b8b000-73b93000 rw-p 00000000 00:05 10267640 /dev/ashmem/dalvik-non moving space (deleted)
+73b93000-77b8b000 ---p 00008000 00:05 10267640 /dev/ashmem/dalvik-non moving space (deleted)
+77b8b000-97b8b000 rw-p 00000000 00:05 10267645 /dev/ashmem/dalvik-free list large object space (deleted)
+97b8b000-99b8b000 rw-p 00000000 00:05 10270989 /dev/ashmem/dalvik-data-code-cache (deleted)
+99b8b000-9bb8b000 r-xp 00000000 00:05 10270990 /dev/ashmem/dalvik-jit-code-cache (deleted)
+ebad6000-ebad7000 ---p 00000000 00:05 10269717 /dev/ashmem/dalvik-Sentinel fault page (deleted)
+7ffb6e000-7ffb76000 rw-s 000e5000 00:10 20630 /dev/kgsl-3d0
+7ffb76000-7ffb78000 rw-s 000e0000 00:10 20630 /dev/kgsl-3d0
+7ffbc3000-7ffbc4000 rw-s 000e8000 00:10 20630 /dev/kgsl-3d0
+7ffbc4000-7ffbc5000 rw-s 000e7000 00:10 20630 /dev/kgsl-3d0
+7ffbc6000-7ffbce000 rw-s 000e4000 00:10 20630 /dev/kgsl-3d0
+7ffbd0000-7ffbd2000 rw-s 000df000 00:10 20630 /dev/kgsl-3d0
+7ffbd2000-7ffbd4000 rw-s 000de000 00:10 20630 /dev/kgsl-3d0
+7ffbd4000-7ffbd6000 rw-s 000dd000 00:10 20630 /dev/kgsl-3d0
+7ffbd6000-7ffbd8000 rw-s 000dc000 00:10 20630 /dev/kgsl-3d0
+7ffbd8000-7ffbda000 rw-s 000db000 00:10 20630 /dev/kgsl-3d0
+7ffbda000-7ffbdc000 rw-s 000da000 00:10 20630 /dev/kgsl-3d0
+7ffbdd000-7ffbde000 rw-s 000ec000 00:10 20630 /dev/kgsl-3d0
+7ffbde000-7ffbe0000 rw-s 000d8000 00:10 20630 /dev/kgsl-3d0
+7ffce1000-7ffce2000 rw-s 000e6000 00:10 20630 /dev/kgsl-3d0
+7ffce2000-7ffce4000 rw-s 000d9000 00:10 20630 /dev/kgsl-3d0
+7ffce4000-7ffce8000 rw-s 000d4000 00:10 20630 /dev/kgsl-3d0
+7ffce8000-7ffcf8000 rw-s 000d2000 00:10 20630 /dev/kgsl-3d0
+7ffcf8000-7ffd08000 rw-s 000d1000 00:10 20630 /dev/kgsl-3d0
+7ffd08000-7ffd10000 rw-s 000d0000 00:10 20630 /dev/kgsl-3d0
+7ffd14000-7ffd18000 rw-s 000cd000 00:10 20630 /dev/kgsl-3d0
+7ffd18000-7ffd28000 rw-s 000cc000 00:10 20630 /dev/kgsl-3d0
+7ffd28000-7ffd38000 rw-s 000cb000 00:10 20630 /dev/kgsl-3d0
+7ffd38000-7ffd48000 rw-s 000ca000 00:10 20630 /dev/kgsl-3d0
+7ffd48000-7ffd58000 rw-s 000c9000 00:10 20630 /dev/kgsl-3d0
+7ffd58000-7ffd68000 rw-s 000c8000 00:10 20630 /dev/kgsl-3d0
+7ffd68000-7ffd6c000 rw-s 000c7000 00:10 20630 /dev/kgsl-3d0
+7ffdb1000-7ffdb2000 rw-s 000e3000 00:10 20630 /dev/kgsl-3d0
+7ffdb4000-7ffdb5000 rw-s 000e2000 00:10 20630 /dev/kgsl-3d0
+7ffdb5000-7ffdb7000 rw-s 000d7000 00:10 20630 /dev/kgsl-3d0
+7ffdb7000-7ffdb8000 rw-s 000c2000 00:10 20630 /dev/kgsl-3d0
+7ffdb8000-7ffdbc000 rw-s 000c0000 00:10 20630 /dev/kgsl-3d0
+7ffdbc000-7ffdc0000 rw-s 000be000 00:10 20630 /dev/kgsl-3d0
+7ffdc0000-7ffe00000 rw-s 000bb000 00:10 20630 /dev/kgsl-3d0
+7ffe00000-7ffe20000 rw-s 000ba000 00:10 20630 /dev/kgsl-3d0
+7ffe20000-7ffee0000 rw-s 000b9000 00:10 20630 /dev/kgsl-3d0
+7ffee1000-7ffee3000 rw-s 000c1000 00:10 20630 /dev/kgsl-3d0
+7ffee3000-7ffee4000 rw-s 000bf000 00:10 20630 /dev/kgsl-3d0
+7ffee4000-7ffee8000 rw-s 000bd000 00:10 20630 /dev/kgsl-3d0
+7ffee8000-7ffee9000 rw-s 000bc000 00:10 20630 /dev/kgsl-3d0
+7ffeea000-7ffeeb000 rw-s 000e1000 00:10 20630 /dev/kgsl-3d0
+7ffeeb000-7ffeec000 rw-s 000b6000 00:10 20630 /dev/kgsl-3d0
+7ffeec000-7ffeed000 rw-s 000b5000 00:10 20630 /dev/kgsl-3d0
+7ffeed000-7ffeee000 rw-s 000b4000 00:10 20630 /dev/kgsl-3d0
+7ffeee000-7ffeef000 rw-s 000b3000 00:10 20630 /dev/kgsl-3d0
+7ffeef000-7ffef0000 rw-s 000b2000 00:10 20630 /dev/kgsl-3d0
+7ffef0000-7ffef1000 rw-s 000b1000 00:10 20630 /dev/kgsl-3d0
+7ffef1000-7ffef2000 rw-s 000b0000 00:10 20630 /dev/kgsl-3d0
+7ffef2000-7ffef3000 rw-s 000af000 00:10 20630 /dev/kgsl-3d0
+7ffef3000-7ffef4000 rw-s 000ae000 00:10 20630 /dev/kgsl-3d0
+7ffef4000-7ffef5000 rw-s 000ad000 00:10 20630 /dev/kgsl-3d0
+7ffef5000-7ffef6000 rw-s 000ac000 00:10 20630 /dev/kgsl-3d0
+7ffef6000-7ffef7000 rw-s 000ab000 00:10 20630 /dev/kgsl-3d0
+7ffef7000-7ffef8000 rw-s 000aa000 00:10 20630 /dev/kgsl-3d0
+7ffef8000-7ffef9000 rw-s 000a9000 00:10 20630 /dev/kgsl-3d0
+7ffef9000-7ffefa000 rw-s 000a8000 00:10 20630 /dev/kgsl-3d0
+7ffefa000-7ffefb000 rw-s 000a7000 00:10 20630 /dev/kgsl-3d0
+7ffefb000-7ffefc000 rw-s 000a6000 00:10 20630 /dev/kgsl-3d0
+7ffefc000-7ffefd000 rw-s 000a5000 00:10 20630 /dev/kgsl-3d0
+7ffefd000-7ffefe000 rw-s 000a4000 00:10 20630 /dev/kgsl-3d0
+7ffefe000-7ffeff000 rw-s 000a3000 00:10 20630 /dev/kgsl-3d0
+7ffeff000-7fff00000 rw-s 000a2000 00:10 20630 /dev/kgsl-3d0
+7fff00000-7fff01000 rw-s 000a1000 00:10 20630 /dev/kgsl-3d0
+7fff01000-7fff02000 rw-s 000a0000 00:10 20630 /dev/kgsl-3d0
+7fff02000-7fff03000 rw-s 0009f000 00:10 20630 /dev/kgsl-3d0
+7fff03000-7fff04000 rw-s 0009e000 00:10 20630 /dev/kgsl-3d0
+7fff04000-7fff05000 rw-s 0009d000 00:10 20630 /dev/kgsl-3d0
+7fff05000-7fff06000 rw-s 0009c000 00:10 20630 /dev/kgsl-3d0
+7fff06000-7fff07000 rw-s 0009b000 00:10 20630 /dev/kgsl-3d0
+7fff07000-7fff08000 rw-s 0009a000 00:10 20630 /dev/kgsl-3d0
+7fff08000-7fff09000 rw-s 00099000 00:10 20630 /dev/kgsl-3d0
+7fff09000-7fff0a000 rw-s 00098000 00:10 20630 /dev/kgsl-3d0
+7fff0a000-7fff0b000 rw-s 00097000 00:10 20630 /dev/kgsl-3d0
+7fff0b000-7fff0c000 rw-s 00096000 00:10 20630 /dev/kgsl-3d0
+7fff0c000-7fff0d000 rw-s 00095000 00:10 20630 /dev/kgsl-3d0
+7fff0d000-7fff0e000 rw-s 00094000 00:10 20630 /dev/kgsl-3d0
+7fff0e000-7fff0f000 rw-s 00093000 00:10 20630 /dev/kgsl-3d0
+7fff0f000-7fff10000 rw-s 00092000 00:10 20630 /dev/kgsl-3d0
+7fff10000-7fff11000 rw-s 00091000 00:10 20630 /dev/kgsl-3d0
+7fff11000-7fff12000 rw-s 00090000 00:10 20630 /dev/kgsl-3d0
+7fff12000-7fff13000 rw-s 0008f000 00:10 20630 /dev/kgsl-3d0
+7fff13000-7fff14000 rw-s 0008e000 00:10 20630 /dev/kgsl-3d0
+7fff14000-7fff15000 rw-s 0008d000 00:10 20630 /dev/kgsl-3d0
+7fff15000-7fff16000 rw-s 0008c000 00:10 20630 /dev/kgsl-3d0
+7fff16000-7fff17000 rw-s 0008b000 00:10 20630 /dev/kgsl-3d0
+7fff17000-7fff18000 rw-s 0008a000 00:10 20630 /dev/kgsl-3d0
+7fff18000-7fff19000 rw-s 00089000 00:10 20630 /dev/kgsl-3d0
+7fff19000-7fff1a000 rw-s 00088000 00:10 20630 /dev/kgsl-3d0
+7fff1a000-7fff1b000 rw-s 00087000 00:10 20630 /dev/kgsl-3d0
+7fff1b000-7fff1c000 rw-s 00086000 00:10 20630 /dev/kgsl-3d0
+7fff1c000-7fff1d000 rw-s 00085000 00:10 20630 /dev/kgsl-3d0
+7fff1d000-7fff1e000 rw-s 00084000 00:10 20630 /dev/kgsl-3d0
+7fff1e000-7fff1f000 rw-s 00083000 00:10 20630 /dev/kgsl-3d0
+7fff1f000-7fff20000 rw-s 00082000 00:10 20630 /dev/kgsl-3d0
+7fff20000-7fff21000 rw-s 00081000 00:10 20630 /dev/kgsl-3d0
+7fff21000-7fff22000 rw-s 00080000 00:10 20630 /dev/kgsl-3d0
+7fff22000-7fff23000 rw-s 0007f000 00:10 20630 /dev/kgsl-3d0
+7fff23000-7fff24000 rw-s 0007e000 00:10 20630 /dev/kgsl-3d0
+7fff24000-7fff25000 rw-s 0007d000 00:10 20630 /dev/kgsl-3d0
+7fff25000-7fff26000 rw-s 0007c000 00:10 20630 /dev/kgsl-3d0
+7fff26000-7fff27000 rw-s 0007b000 00:10 20630 /dev/kgsl-3d0
+7fff27000-7fff28000 rw-s 0007a000 00:10 20630 /dev/kgsl-3d0
+7fff28000-7fff29000 rw-s 00079000 00:10 20630 /dev/kgsl-3d0
+7fff29000-7fff2a000 rw-s 00078000 00:10 20630 /dev/kgsl-3d0
+7fff2a000-7fff2b000 rw-s 00077000 00:10 20630 /dev/kgsl-3d0
+7fff2b000-7fff2c000 rw-s 00076000 00:10 20630 /dev/kgsl-3d0
+7fff2c000-7fff2d000 rw-s 00075000 00:10 20630 /dev/kgsl-3d0
+7fff2d000-7fff2e000 rw-s 00074000 00:10 20630 /dev/kgsl-3d0
+7fff2e000-7fff2f000 rw-s 00073000 00:10 20630 /dev/kgsl-3d0
+7fff2f000-7fff30000 rw-s 00072000 00:10 20630 /dev/kgsl-3d0
+7fff30000-7fff31000 rw-s 00071000 00:10 20630 /dev/kgsl-3d0
+7fff31000-7fff32000 rw-s 00070000 00:10 20630 /dev/kgsl-3d0
+7fff32000-7fff33000 rw-s 0006f000 00:10 20630 /dev/kgsl-3d0
+7fff33000-7fff34000 rw-s 0006e000 00:10 20630 /dev/kgsl-3d0
+7fff34000-7fff35000 rw-s 0006d000 00:10 20630 /dev/kgsl-3d0
+7fff35000-7fff36000 rw-s 0006c000 00:10 20630 /dev/kgsl-3d0
+7fff36000-7fff37000 rw-s 0006b000 00:10 20630 /dev/kgsl-3d0
+7fff37000-7fff38000 rw-s 0006a000 00:10 20630 /dev/kgsl-3d0
+7fff38000-7fff39000 rw-s 00069000 00:10 20630 /dev/kgsl-3d0
+7fff39000-7fff3a000 rw-s 00068000 00:10 20630 /dev/kgsl-3d0
+7fff3a000-7fff3b000 rw-s 00067000 00:10 20630 /dev/kgsl-3d0
+7fff3b000-7fff3c000 rw-s 00066000 00:10 20630 /dev/kgsl-3d0
+7fff3c000-7fff3d000 rw-s 00065000 00:10 20630 /dev/kgsl-3d0
+7fff3d000-7fff3e000 rw-s 00064000 00:10 20630 /dev/kgsl-3d0
+7fff3e000-7fff3f000 rw-s 00063000 00:10 20630 /dev/kgsl-3d0
+7fff3f000-7fff40000 rw-s 00062000 00:10 20630 /dev/kgsl-3d0
+7fff40000-7fff41000 rw-s 00061000 00:10 20630 /dev/kgsl-3d0
+7fff41000-7fff42000 rw-s 00060000 00:10 20630 /dev/kgsl-3d0
+7fff42000-7fff43000 rw-s 0005f000 00:10 20630 /dev/kgsl-3d0
+7fff43000-7fff44000 rw-s 0005e000 00:10 20630 /dev/kgsl-3d0
+7fff44000-7fff45000 rw-s 0005d000 00:10 20630 /dev/kgsl-3d0
+7fff45000-7fff46000 rw-s 0005c000 00:10 20630 /dev/kgsl-3d0
+7fff46000-7fff47000 rw-s 0005b000 00:10 20630 /dev/kgsl-3d0
+7fff47000-7fff48000 rw-s 0005a000 00:10 20630 /dev/kgsl-3d0
+7fff48000-7fff49000 rw-s 00059000 00:10 20630 /dev/kgsl-3d0
+7fff49000-7fff4a000 rw-s 00058000 00:10 20630 /dev/kgsl-3d0
+7fff4a000-7fff4b000 rw-s 00057000 00:10 20630 /dev/kgsl-3d0
+7fff4b000-7fff4c000 rw-s 00056000 00:10 20630 /dev/kgsl-3d0
+7fff4c000-7fff4d000 rw-s 00055000 00:10 20630 /dev/kgsl-3d0
+7fff4d000-7fff4e000 rw-s 00054000 00:10 20630 /dev/kgsl-3d0
+7fff4e000-7fff4f000 rw-s 00053000 00:10 20630 /dev/kgsl-3d0
+7fff4f000-7fff50000 rw-s 00052000 00:10 20630 /dev/kgsl-3d0
+7fff50000-7fff51000 rw-s 00051000 00:10 20630 /dev/kgsl-3d0
+7fff51000-7fff52000 rw-s 00050000 00:10 20630 /dev/kgsl-3d0
+7fff52000-7fff53000 rw-s 0004f000 00:10 20630 /dev/kgsl-3d0
+7fff53000-7fff54000 rw-s 0004e000 00:10 20630 /dev/kgsl-3d0
+7fff54000-7fff55000 rw-s 0004d000 00:10 20630 /dev/kgsl-3d0
+7fff55000-7fff56000 rw-s 0004c000 00:10 20630 /dev/kgsl-3d0
+7fff56000-7fff57000 rw-s 0004b000 00:10 20630 /dev/kgsl-3d0
+7fff57000-7fff58000 rw-s 0004a000 00:10 20630 /dev/kgsl-3d0
+7fff58000-7fff59000 rw-s 00049000 00:10 20630 /dev/kgsl-3d0
+7fff59000-7fff5a000 rw-s 00048000 00:10 20630 /dev/kgsl-3d0
+7fff5a000-7fff5b000 rw-s 00047000 00:10 20630 /dev/kgsl-3d0
+7fff5b000-7fff5c000 rw-s 00046000 00:10 20630 /dev/kgsl-3d0
+7fff5c000-7fff5d000 rw-s 00045000 00:10 20630 /dev/kgsl-3d0
+7fff5d000-7fff5e000 rw-s 00044000 00:10 20630 /dev/kgsl-3d0
+7fff5e000-7fff5f000 rw-s 00043000 00:10 20630 /dev/kgsl-3d0
+7fff5f000-7fff60000 rw-s 00042000 00:10 20630 /dev/kgsl-3d0
+7fff60000-7fff61000 rw-s 00041000 00:10 20630 /dev/kgsl-3d0
+7fff61000-7fff62000 rw-s 00040000 00:10 20630 /dev/kgsl-3d0
+7fff62000-7fff63000 rw-s 0003f000 00:10 20630 /dev/kgsl-3d0
+7fff63000-7fff64000 rw-s 0003e000 00:10 20630 /dev/kgsl-3d0
+7fff64000-7fff65000 rw-s 0003d000 00:10 20630 /dev/kgsl-3d0
+7fff65000-7fff66000 rw-s 0003c000 00:10 20630 /dev/kgsl-3d0
+7fff66000-7fff67000 rw-s 0003b000 00:10 20630 /dev/kgsl-3d0
+7fff67000-7fff68000 rw-s 0003a000 00:10 20630 /dev/kgsl-3d0
+7fff68000-7fff69000 rw-s 00039000 00:10 20630 /dev/kgsl-3d0
+7fff69000-7fff6a000 rw-s 00038000 00:10 20630 /dev/kgsl-3d0
+7fff6a000-7fff6b000 rw-s 00037000 00:10 20630 /dev/kgsl-3d0
+7fff6b000-7fff6c000 rw-s 00036000 00:10 20630 /dev/kgsl-3d0
+7fff6c000-7fff6d000 rw-s 00035000 00:10 20630 /dev/kgsl-3d0
+7fff6d000-7fff6e000 rw-s 00034000 00:10 20630 /dev/kgsl-3d0
+7fff6e000-7fff6f000 rw-s 00033000 00:10 20630 /dev/kgsl-3d0
+7fff6f000-7fff70000 rw-s 00032000 00:10 20630 /dev/kgsl-3d0
+7fff70000-7fff71000 rw-s 00031000 00:10 20630 /dev/kgsl-3d0
+7fff71000-7fff72000 rw-s 00030000 00:10 20630 /dev/kgsl-3d0
+7fff72000-7fff73000 rw-s 0002f000 00:10 20630 /dev/kgsl-3d0
+7fff73000-7fff74000 rw-s 0002e000 00:10 20630 /dev/kgsl-3d0
+7fff74000-7fff75000 rw-s 0002d000 00:10 20630 /dev/kgsl-3d0
+7fff75000-7fff76000 rw-s 0002c000 00:10 20630 /dev/kgsl-3d0
+7fff76000-7fff77000 rw-s 0002b000 00:10 20630 /dev/kgsl-3d0
+7fff77000-7fff78000 rw-s 0002a000 00:10 20630 /dev/kgsl-3d0
+7fff78000-7fff79000 rw-s 00029000 00:10 20630 /dev/kgsl-3d0
+7fff79000-7fff7a000 rw-s 00028000 00:10 20630 /dev/kgsl-3d0
+7fff7a000-7fff7b000 rw-s 00027000 00:10 20630 /dev/kgsl-3d0
+7fff7b000-7fff7c000 rw-s 00026000 00:10 20630 /dev/kgsl-3d0
+7fff7c000-7fff7d000 rw-s 00025000 00:10 20630 /dev/kgsl-3d0
+7fff7d000-7fff7e000 rw-s 00024000 00:10 20630 /dev/kgsl-3d0
+7fff7e000-7fff7f000 rw-s 00023000 00:10 20630 /dev/kgsl-3d0
+7fff7f000-7fff80000 rw-s 00022000 00:10 20630 /dev/kgsl-3d0
+7fff80000-7fff90000 rw-s 00019000 00:10 20630 /dev/kgsl-3d0
+7fff90000-7fffb0000 rw-s 00018000 00:10 20630 /dev/kgsl-3d0
+7fffb1000-7fffb2000 rw-s 00021000 00:10 20630 /dev/kgsl-3d0
+7fffb2000-7fffb3000 rw-s 00020000 00:10 20630 /dev/kgsl-3d0
+7fffb3000-7fffb4000 rw-s 0001f000 00:10 20630 /dev/kgsl-3d0
+7fffba000-7fffbe000 rw-s 0001b000 00:10 20630 /dev/kgsl-3d0
+7fffbe000-7fffbf000 rw-s 0001a000 00:10 20630 /dev/kgsl-3d0
+7fffbf000-7fffc0000 rw-s 00017000 00:10 20630 /dev/kgsl-3d0
+7fffc0000-7fffe0000 rw-s 00016000 00:10 20630 /dev/kgsl-3d0
+7fffe0000-7fffe1000 rw-s 00014000 00:10 20630 /dev/kgsl-3d0
+7fffe1000-7fffe5000 rw-s 00013000 00:10 20630 /dev/kgsl-3d0
+7fffe5000-7fffe6000 rw-s 00012000 00:10 20630 /dev/kgsl-3d0
+7fffe6000-7fffe7000 rw-s 00011000 00:10 20630 /dev/kgsl-3d0
+7fffe7000-7fffe8000 rw-s 00010000 00:10 20630 /dev/kgsl-3d0
+7fffe8000-7fffe9000 rw-s 0000f000 00:10 20630 /dev/kgsl-3d0
+7fffe9000-7fffea000 rw-s 0000e000 00:10 20630 /dev/kgsl-3d0
+7fffea000-7fffeb000 rw-s 0000d000 00:10 20630 /dev/kgsl-3d0
+7fffeb000-7fffec000 rw-s 0000c000 00:10 20630 /dev/kgsl-3d0
+7fffec000-7ffff0000 rw-s 0000b000 00:10 20630 /dev/kgsl-3d0
+7ffff0000-7ffff1000 rw-s 0000a000 00:10 20630 /dev/kgsl-3d0
+7ffff1000-7ffff5000 rw-s 00009000 00:10 20630 /dev/kgsl-3d0
+7ffff5000-7ffff6000 rw-s 00008000 00:10 20630 /dev/kgsl-3d0
+7ffff6000-7ffff7000 rw-s 00007000 00:10 20630 /dev/kgsl-3d0
+7ffff7000-7ffff8000 rw-s 00006000 00:10 20630 /dev/kgsl-3d0
+7ffff8000-7ffff9000 rw-s 00005000 00:10 20630 /dev/kgsl-3d0
+7ffff9000-7ffffa000 rw-s 00004000 00:10 20630 /dev/kgsl-3d0
+7ffffa000-7ffffb000 rw-s 00003000 00:10 20630 /dev/kgsl-3d0
+7ffffb000-7ffffc000 rw-s 00002000 00:10 20630 /dev/kgsl-3d0
+7ffffc000-800000000 rw-s 00001000 00:10 20630 /dev/kgsl-3d0
+5ff1d4f000-5ff1d54000 r-xp 00000000 fc:00 3419 /system/bin/app_process64
+5ff1d6e000-5ff1d6f000 r--p 0000f000 fc:00 3419 /system/bin/app_process64
+5ff1d6f000-5ff1d71000 rw-p 00000000 00:00 0
+704defa000-704defb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704defb000-704defc000 ---p 00000000 00:00 0
+704defc000-704e000000 rw-p 00000000 00:00 0
+704e000000-704e400000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+704e455000-704e456000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704e456000-704e457000 ---p 00000000 00:00 0
+704e457000-704e553000 rw-p 00000000 00:00 0
+704e553000-704e651000 r--p 00000000 00:10 16029 /dev/hwbinder
+704e651000-704e65f000 r-xp 00000000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e65f000-704e660000 r--p 0000e000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e660000-704e661000 rw-p 0000f000 fc:01 1040 /vendor/lib64/egl/eglSubDriverAndroid.so
+704e69d000-704e69e000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704e69e000-704e79b000 rw-p 00000000 00:00 0
+704e79b000-704f79b000 rw-s 00000000 00:05 10271021 /dev/ashmem/AudioFlinger::Client(29312) (deleted)
+704f79b000-704f79c000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704f79c000-704f899000 rw-p 00000000 00:00 0
+704f899000-704f89a000 ---p 00000000 00:00 0 [anon:thread stack guard]
+704f89a000-704f89b000 ---p 00000000 00:00 0
+704f89b000-704f997000 rw-p 00000000 00:00 0
+704f997000-704f9ee000 r-xp 00000000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704f9ee000-704f9fd000 ---p 00000000 00:00 0
+704f9fd000-704fa00000 r--p 00056000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa00000-704fa01000 rw-p 00059000 103:1d 1737338 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/lib/arm64/libgame.so
+704fa01000-704fa19000 rw-p 00000000 00:00 0 [anon:.bss]
+704fa40000-70507e7000 r-xp 00000000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+70507e7000-70507fc000 ---p 00000000 00:00 0
+70507fc000-7050835000 r--p 00da7000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+7050835000-705083a000 rw-p 00de0000 fc:01 1026 /vendor/lib64/libllvm-glnext.so
+705083a000-7050855000 rw-p 00000000 00:00 0 [anon:.bss]
+705089b000-7050f19000 r-xp 00000000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f19000-7050f22000 r--p 0067e000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f22000-7050f29000 rw-p 00687000 fc:01 1039 /vendor/lib64/egl/libGLESv2_adreno.so
+7050f29000-7050f2c000 rw-p 00000000 00:00 0 [anon:.bss]
+7050f83000-7050fbc000 r-xp 00000000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbc000-7050fbd000 r--p 00039000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbd000-7050fbe000 rw-p 0003a000 fc:01 1041 /vendor/lib64/egl/libGLESv1_CM_adreno.so
+7050fbe000-7050fbf000 rw-p 00000000 00:00 0 [anon:.bss]
+7050fc6000-705111d000 r-xp 00000000 fc:01 865 /vendor/lib64/libgsl.so
+705111d000-705111e000 r--p 00157000 fc:01 865 /vendor/lib64/libgsl.so
+705111e000-705111f000 rw-p 00158000 fc:01 865 /vendor/lib64/libgsl.so
+705111f000-7051120000 rw-p 00000000 00:00 0 [anon:.bss]
+7051146000-705115d000 r-xp 00000000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+705115d000-7051175000 ---p 00000000 00:00 0
+7051175000-7051176000 r--p 0001f000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+7051176000-7051177000 rw-p 00020000 fc:00 2587 /system/lib64/vndk-sp-28/libz.so
+705119f000-70511ac000 r-xp 00000000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ac000-70511ad000 r--p 0000d000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ad000-70511ae000 rw-p 0000e000 fc:01 886 /vendor/lib64/libadreno_utils.so
+70511ae000-70511b0000 rw-p 00000000 00:00 0 [anon:.bss]
+70511c0000-70511d7000 r-xp 00000000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d7000-70511d8000 r--p 00017000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d8000-70511d9000 rw-p 00018000 fc:01 1044 /vendor/lib64/egl/libEGL_adreno.so
+70511d9000-70511da000 rw-p 00000000 00:00 0 [anon:.bss]
+705120a000-705120d000 r-xp 00000000 fc:01 972 /vendor/lib64/libdrmutils.so
+705120d000-7051229000 ---p 00000000 00:00 0
+7051229000-705122a000 r--p 0000f000 fc:01 972 /vendor/lib64/libdrmutils.so
+705122a000-705122b000 rw-p 00010000 fc:01 972 /vendor/lib64/libdrmutils.so
+705125a000-705125c000 r-xp 00000000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+705125c000-7051279000 ---p 00000000 00:00 0
+7051279000-705127a000 r--p 0000f000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+705127a000-705127b000 rw-p 00010000 fc:01 1046 /vendor/lib64/libqdMetaData.so
+7051286000-7051297000 r-xp 00000000 fc:01 1024 /vendor/lib64/libdrm.so
+7051297000-70512b5000 ---p 00000000 00:00 0
+70512b5000-70512b6000 r--p 0001f000 fc:01 1024 /vendor/lib64/libdrm.so
+70512b6000-70512b7000 rw-p 00020000 fc:01 1024 /vendor/lib64/libdrm.so
+70512cb000-70512de000 r-xp 00000000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+70512de000-70512fa000 ---p 00000000 00:00 0
+70512fa000-70512fb000 r--p 0001f000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+70512fb000-70512fc000 rw-p 00020000 fc:01 1008 /vendor/lib64/hw/gralloc.msm8998.so
+7051326000-7051327000 ---p 00000000 00:00 0 [anon:thread stack guard]
+7051327000-7051328000 ---p 00000000 00:00 0
+7051328000-7051424000 rw-p 00000000 00:00 0
+7051424000-705143d000 r--p 00000000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705143d000-7051480000 r-xp 00019000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+7051480000-7051494000 r--p 00211000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+7051494000-705149f000 r--p 000c5000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+705149f000-70514a2000 r--p 00032000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70514a2000-70514a5000 r--p 0002b000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70514a5000-70514ac000 r--p 0003f000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70514ac000-70514b2000 r--p 00044000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70514b2000-70514bd000 r--p 00035000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70514bd000-70514f4000 r--p 0060f000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70514f4000-70514fe000 r--p 00054000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70514fe000-70514ff000 r--p 0000c000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70514ff000-7051500000 r--p 0000e000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+7051500000-7051501000 r--p 00004000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+7051501000-7051502000 r--p 00004000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+7051502000-7051503000 r--p 00001000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+7051503000-7051504000 rw-p 00000000 00:00 0 [anon:.bss]
+7051504000-7051579000 r--s 00000000 fc:00 790 /system/framework/oat/arm64/org.apache.http.legacy.boot.vdex
+7051579000-705157a000 r--p 0005c000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705157a000-705157b000 rw-p 0005d000 fc:00 739 /system/framework/oat/arm64/org.apache.http.legacy.boot.odex
+705158b000-7057f4d000 ---p 00000000 00:00 0
+7057f4d000-7057f4f000 r-xp 00000000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f4f000-7057f6c000 ---p 00000000 00:00 0
+7057f6c000-7057f6d000 r--p 0000f000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f6d000-7057f6e000 rw-p 00010000 fc:00 2646 /system/lib64/libwebviewchromium_loader.so
+7057f76000-7057f96000 r--s 00000000 00:10 16615 /dev/__properties__/u:object_r:hwservicemanager_prop:s0
+7057f96000-7057fb6000 r--s 00000000 00:10 16639 /dev/__properties__/u:object_r:public_vendor_default_prop:s0
+7057fb6000-7058004000 r--s 00000000 fc:00 1112 /system/usr/hyphen-data/hyph-hu.hyb
+7058004000-7058024000 r-xp 00000000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058024000-7058043000 ---p 00000000 00:00 0
+7058043000-7058044000 r--p 0002f000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058044000-7058045000 rw-p 00030000 fc:00 2354 /system/lib64/libcompiler_rt.so
+7058045000-70580b2000 rw-p 00000000 00:00 0 [anon:.bss]
+70580bd000-70580dd000 rw-p 00000000 00:05 10265386 /dev/ashmem/dalvik-LinearAlloc (deleted)
+70580dd000-70580df000 r-xp 00000000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+70580df000-70580fc000 ---p 00000000 00:00 0
+70580fc000-70580fd000 r--p 0000f000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+70580fd000-70580fe000 rw-p 00010000 fc:00 2597 /system/lib64/vndk-sp-28/libhardware.so
+705810e000-705811f000 r-xp 00000000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+705811f000-705813d000 ---p 00000000 00:00 0
+705813d000-705813e000 r--p 0001f000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+705813e000-705813f000 rw-p 00020000 fc:00 2589 /system/lib64/vndk-sp-28/libbase.so
+7058140000-7058167000 r-xp 00000000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+7058167000-705817d000 ---p 00000000 00:00 0
+705817d000-705817f000 r--p 0002e000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+705817f000-7058180000 rw-p 00030000 fc:00 2572 /system/lib64/vndk-sp-28/libhwbinder.so
+705818c000-705818d000 r-xp 00000000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+705818d000-70581ab000 ---p 00000000 00:00 0
+70581ab000-70581ac000 r--p 0000f000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581ac000-70581ad000 rw-p 00010000 fc:00 2584 /system/lib64/vndk-sp-28/android.hardware.graphics.common@1.0.so
+70581b7000-70581d7000 r--s 00000000 00:10 16619 /dev/__properties__/u:object_r:log_prop:s0
+70581d7000-7058237000 r-xp 00000000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+7058237000-7058255000 ---p 00000000 00:00 0
+7058255000-705825d000 r--p 00068000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+705825d000-705825e000 rw-p 00070000 fc:00 2574 /system/lib64/vndk-sp-28/libhidltransport.so
+7058260000-7058284000 r--s 00000000 fc:00 1138 /system/usr/hyphen-data/hyph-nn.hyb
+7058284000-70582a0000 r-xp 00000000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582a0000-70582b3000 ---p 00000000 00:00 0
+70582b3000-70582b4000 r--p 0001f000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582b4000-70582b5000 rw-p 00020000 fc:00 2576 /system/lib64/vndk-sp-28/libutils.so
+70582c4000-7058391000 r-xp 00000000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+7058391000-70583ad000 ---p 00000000 00:00 0
+70583ad000-70583b7000 r--p 000d6000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+70583b7000-70583b8000 rw-p 000e0000 fc:00 2568 /system/lib64/vndk-sp-28/libc++.so
+70583b8000-70583bb000 rw-p 00000000 00:00 0 [anon:.bss]
+70583cd000-70583e4000 r-xp 00000000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583e4000-70583f9000 ---p 00000000 00:00 0
+70583f9000-70583fb000 r--p 0001e000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+70583fb000-70583fc000 rw-p 00020000 fc:00 2580 /system/lib64/vndk-sp-28/android.hardware.graphics.mapper@2.0.so
+705841b000-7058421000 r-xp 00000000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+7058421000-705843a000 ---p 00000000 00:00 0
+705843a000-705843b000 r--p 0000f000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705843b000-705843c000 rw-p 00010000 fc:01 1001 /vendor/lib64/hw/android.hardware.graphics.mapper@2.0-impl.so
+705844f000-7058473000 r--s 00000000 fc:00 1150 /system/usr/hyphen-data/hyph-nb.hyb
+7058473000-7058495000 r-xp 00000000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+7058495000-70584b1000 ---p 00000000 00:00 0
+70584b1000-70584b3000 r--p 0002e000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+70584b3000-70584b4000 rw-p 00030000 fc:00 2582 /system/lib64/vndk-sp-28/libhidlbase.so
+70584cd000-70584df000 r-xp 00000000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+70584df000-70584fb000 ---p 00000000 00:00 0
+70584fb000-70584fd000 r--p 0001e000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+70584fd000-70584fe000 rw-p 00020000 fc:00 2595 /system/lib64/vndk-sp-28/libcutils.so
+7058519000-7058537000 r--s 00000000 fc:00 1124 /system/usr/hyphen-data/hyph-de-ch-1901.hyb
+7058537000-7059fd1000 r--s 0070b000 fc:00 989 /system/framework/framework-res.apk
+7059fd1000-705a013000 r-xp 00000000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a013000-705a02b000 ---p 00000000 00:00 0
+705a02b000-705a02d000 r--p 0004e000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a02d000-705a02f000 rw-p 00050000 fc:00 2610 /system/lib64/libjavacrypto.so
+705a041000-705a05f000 r--s 00000000 fc:00 1128 /system/usr/hyphen-data/hyph-de-1996.hyb
+705a05f000-705a06a000 r-xp 00000000 fc:00 2917 /system/lib64/libsoundpool.so
+705a06a000-705a07e000 ---p 00000000 00:00 0
+705a07e000-705a07f000 r--p 0000f000 fc:00 2917 /system/lib64/libsoundpool.so
+705a07f000-705a080000 rw-p 00010000 fc:00 2917 /system/lib64/libsoundpool.so
+705a087000-705a102000 r--s 00000000 fc:00 1246 /system/usr/share/zoneinfo/tzdata
+705a102000-705a863000 r--s 00000000 fc:00 101 /system/fonts/NotoColorEmoji.ttf
+705a863000-705c000000 r--s 00000000 fc:00 251 /system/fonts/NotoSerifCJK-Regular.ttc
+705c000000-705c200000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705c209000-705c227000 r--s 00000000 fc:00 1077 /system/usr/hyphen-data/hyph-de-1901.hyb
+705c227000-705c26e000 r--s 02284000 fc:00 989 /system/framework/framework-res.apk
+705c26e000-705d43e000 r--s 00000000 fc:00 95 /system/fonts/NotoSansCJK-Regular.ttc
+705d43e000-705d4ec000 r--s 00000000 fc:00 278 /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf
+705d4ec000-705d548000 r--s 00000000 fc:00 233 /system/fonts/NotoSansTibetan-Bold.ttf
+705d548000-705d5ab000 r--s 00000000 fc:00 177 /system/fonts/NotoSansTibetan-Regular.ttf
+705d5ab000-705d627000 r--s 00000000 fc:00 197 /system/fonts/NotoSansEgyptianHieroglyphs-Regular.ttf
+705d627000-705d6a2000 r--s 00000000 fc:00 76 /system/fonts/NotoSansCuneiform-Regular.ttf
+705d6a2000-705d6f3000 r--s 00000000 fc:00 67 /system/fonts/RobotoCondensed-BoldItalic.ttf
+705d6f3000-705d73e000 r--s 00000000 fc:00 199 /system/fonts/RobotoCondensed-Bold.ttf
+705d73e000-705d78f000 r--s 00000000 fc:00 230 /system/fonts/RobotoCondensed-MediumItalic.ttf
+705d78f000-705d7da000 r--s 00000000 fc:00 92 /system/fonts/RobotoCondensed-Medium.ttf
+705d7da000-705d82b000 r--s 00000000 fc:00 128 /system/fonts/RobotoCondensed-Italic.ttf
+705d82b000-705d875000 r--s 00000000 fc:00 164 /system/fonts/RobotoCondensed-Regular.ttf
+705d875000-705d8c7000 r--s 00000000 fc:00 292 /system/fonts/RobotoCondensed-LightItalic.ttf
+705d8c7000-705d919000 r--s 00000000 fc:00 85 /system/fonts/Roboto-BoldItalic.ttf
+705d919000-705d964000 r--s 00000000 fc:00 175 /system/fonts/Roboto-Bold.ttf
+705d964000-705d9b5000 r--s 00000000 fc:00 266 /system/fonts/Roboto-BlackItalic.ttf
+705d9b5000-705da00000 r--s 00000000 fc:00 187 /system/fonts/Roboto-Black.ttf
+705da00000-705dc00000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705dc1d000-705dc6e000 r--s 00000000 fc:00 148 /system/fonts/Roboto-MediumItalic.ttf
+705dc6e000-705dcb9000 r--s 00000000 fc:00 284 /system/fonts/Roboto-Medium.ttf
+705dcb9000-705dd0a000 r--s 00000000 fc:00 105 /system/fonts/Roboto-Italic.ttf
+705dd0a000-705dd55000 r--s 00000000 fc:00 156 /system/fonts/Roboto-Regular.ttf
+705dd55000-705dda7000 r--s 00000000 fc:00 217 /system/fonts/Roboto-LightItalic.ttf
+705dda7000-705ddf8000 r--s 00000000 fc:00 166 /system/fonts/Roboto-ThinItalic.ttf
+705ddf8000-705ddf9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705ddf9000-705ddfa000 ---p 00000000 00:00 0
+705ddfa000-705def6000 rw-p 00000000 00:00 0
+705def6000-705f5ec000 r--s 00000000 fc:00 1350 /system/usr/icu/icudt60l.dat
+705f5ec000-705f5ed000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f5ed000-705f5ee000 ---p 00000000 00:00 0
+705f5ee000-705f6ea000 rw-p 00000000 00:00 0
+705f6ea000-705f7e8000 r--p 00000000 00:10 20636 /dev/binder
+705f7e8000-705f7e9000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f7e9000-705f7ea000 ---p 00000000 00:00 0
+705f7ea000-705f8ee000 rw-p 00000000 00:00 0
+705f8ee000-705f8ef000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f8ef000-705f8f0000 ---p 00000000 00:00 0
+705f8f0000-705f9f4000 rw-p 00000000 00:00 0
+705f9f4000-705f9f5000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705f9f5000-705f9f6000 ---p 00000000 00:00 0
+705f9f6000-705fafa000 rw-p 00000000 00:00 0
+705fafa000-705fafb000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705fafb000-705fafc000 ---p 00000000 00:00 0
+705fafc000-705fc00000 rw-p 00000000 00:00 0
+705fc00000-705fe00000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+705fe01000-705fe4c000 r--s 00000000 fc:00 97 /system/fonts/Roboto-Light.ttf
+705fe4c000-705fe4d000 ---p 00000000 00:00 0 [anon:thread stack guard]
+705fe4d000-705fe4e000 ---p 00000000 00:00 0
+705fe4e000-705ff4a000 rw-p 00000000 00:00 0
+705ff4a000-705ff4b000 ---p 00000000 00:05 10270991 /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+705ff4b000-705ff4c000 ---p 00001000 00:05 10270991 /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+705ff4c000-706004b000 rw-p 00002000 00:05 10270991 /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+706004b000-706010f000 r-xp 00000000 fc:00 2390 /system/lib64/libvixl-arm64.so
+706010f000-7060120000 ---p 00000000 00:00 0
+7060120000-7060125000 r--p 000cb000 fc:00 2390 /system/lib64/libvixl-arm64.so
+7060125000-7060126000 rw-p 000d0000 fc:00 2390 /system/lib64/libvixl-arm64.so
+7060126000-706012d000 rw-p 00000000 00:00 0 [anon:.bss]
+7060135000-7060151000 r--s 00000000 fc:01 1180 /vendor/overlay/framework-res__auto_generated_rro.apk
+7060151000-7060263000 r-xp 00000000 fc:00 2669 /system/lib64/libvixl-arm.so
+7060263000-7060275000 ---p 00000000 00:00 0
+7060275000-706027a000 r--p 0011b000 fc:00 2669 /system/lib64/libvixl-arm.so
+706027a000-706027b000 rw-p 00120000 fc:00 2669 /system/lib64/libvixl-arm.so
+706028b000-706056c000 r-xp 00000000 fc:00 2972 /system/lib64/libart-compiler.so
+706056c000-7060580000 ---p 00000000 00:00 0
+7060580000-7060598000 r--p 002e8000 fc:00 2972 /system/lib64/libart-compiler.so
+7060598000-7060599000 rw-p 00300000 fc:00 2972 /system/lib64/libart-compiler.so
+7060599000-70605a0000 rw-p 00000000 00:00 0 [anon:.bss]
+70605b0000-70605d0000 r--s 00000000 00:10 16571 /dev/__properties__/u:object_r:config_prop:s0
+70605d0000-7060619000 r-xp 00000000 fc:00 2702 /system/lib64/libssl.so
+7060619000-706062d000 ---p 00000000 00:00 0
+706062d000-7060630000 r--p 0004d000 fc:00 2702 /system/lib64/libssl.so
+7060630000-7060631000 rw-p 00050000 fc:00 2702 /system/lib64/libssl.so
+7060647000-7060667000 r--s 00000000 00:10 16595 /dev/__properties__/u:object_r:exported3_radio_prop:s0
+7060667000-706069d000 r-xp 00000000 fc:00 2371 /system/lib64/libopenjdk.so
+706069d000-70606b2000 ---p 00000000 00:00 0
+70606b2000-70606b4000 r--p 0003e000 fc:00 2371 /system/lib64/libopenjdk.so
+70606b4000-70606b6000 rw-p 00040000 fc:00 2371 /system/lib64/libopenjdk.so
+70606bb000-70606db000 r--s 00000000 00:10 16608 /dev/__properties__/u:object_r:exported_system_prop:s0
+70606db000-70606e3000 r-xp 00000000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+70606e3000-70606fa000 ---p 00000000 00:00 0
+70606fa000-70606fb000 r--p 0000f000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+70606fb000-70606fc000 rw-p 00010000 fc:00 2538 /system/lib64/libopenjdkjvm.so
+7060701000-7060722000 r--s 00000000 fc:00 227 /system/fonts/NotoSansAnatolianHieroglyphs-Regular.otf
+7060722000-7061e18000 r--s 00000000 fc:00 1350 /system/usr/icu/icudt60l.dat
+7061e18000-7061e5d000 r-xp 00000000 fc:00 2368 /system/lib64/libjavacore.so
+7061e5d000-7061e71000 ---p 00000000 00:00 0
+7061e71000-7061e73000 r--p 0004e000 fc:00 2368 /system/lib64/libjavacore.so
+7061e73000-7061e75000 rw-p 00050000 fc:00 2368 /system/lib64/libjavacore.so
+7061e75000-7061e76000 rw-p 00000000 00:00 0 [anon:.bss]
+7061e77000-7061e96000 r--s 00000000 fc:00 186 /system/fonts/NotoSansYi-Regular.ttf
+7061e96000-7061e99000 r-xp 00000000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061e99000-7061eb5000 ---p 00000000 00:00 0
+7061eb5000-7061eb6000 r--p 0000f000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061eb6000-7061eb7000 rw-p 00010000 fc:00 2953 /system/lib64/libwebviewchromium_plat_support.so
+7061ebc000-7061edd000 r--s 00000000 fc:00 100 /system/fonts/NotoSansBamum-Regular.ttf
+7061edd000-7061eed000 r-xp 00000000 fc:00 2945 /system/lib64/libRS.so
+7061eed000-7061efc000 ---p 00000000 00:00 0
+7061efc000-7061efd000 r--p 0000f000 fc:00 2945 /system/lib64/libRS.so
+7061efd000-7061efe000 rw-p 00010000 fc:00 2945 /system/lib64/libRS.so
+7061f05000-7061f6b000 r-xp 00000000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f6b000-7061f7a000 ---p 00000000 00:00 0
+7061f7a000-7061f7f000 r--p 0006b000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f7f000-7061f80000 rw-p 00070000 fc:00 2423 /system/lib64/android.hardware.renderscript@1.0.so
+7061f99000-7061f9b000 r-xp 00000000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061f9b000-7061fb8000 ---p 00000000 00:00 0
+7061fb8000-7061fb9000 r--p 0000f000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061fb9000-7061fba000 rw-p 00010000 fc:00 2614 /system/lib64/libOpenSLES.so
+7061fc6000-7061fc8000 r-xp 00000000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fc8000-7061fe5000 ---p 00000000 00:00 0
+7061fe5000-7061fe6000 r--p 0000f000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fe6000-7061fe7000 rw-p 00010000 fc:00 2963 /system/lib64/libOpenMAXAL.so
+7061fe7000-7062000000 r--s 00000000 fc:00 143 /system/fonts/NotoSansBhaiksuki-Regular.otf
+7062000000-7062003000 r-xp 00000000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062003000-706201f000 ---p 00000000 00:00 0
+706201f000-7062020000 r--p 0000f000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062020000-7062021000 rw-p 00010000 fc:00 2447 /system/lib64/libtextclassifier_hash.so
+7062022000-7062042000 rw-p 00000000 00:05 10269731 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062042000-7062077000 r-xp 00000000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+7062077000-7062095000 ---p 00000000 00:00 0
+7062095000-706209b000 r--p 0003a000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+706209b000-706209c000 rw-p 00040000 fc:00 2372 /system/lib64/android.hardware.neuralnetworks@1.0.so
+70620a9000-70620c9000 rw-p 00000000 00:05 10269730 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70620c9000-70620e3000 r-xp 00000000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620e3000-70620f4000 ---p 00000000 00:00 0
+70620f4000-70620f7000 r--p 0001d000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+70620f7000-70620f8000 rw-p 00020000 fc:00 2956 /system/lib64/android.hardware.neuralnetworks@1.1.so
+706210b000-70621d0000 r-xp 00000000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621d0000-70621e3000 ---p 00000000 00:00 0
+70621e3000-70621e5000 r--p 000ce000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621e5000-70621e7000 rw-p 000d0000 fc:00 2387 /system/lib64/libneuralnetworks.so
+70621e7000-7062372000 rw-p 00000000 00:00 0 [anon:.bss]
+7062373000-7062395000 r--s 00000000 fc:00 274 /system/fonts/NotoSerifMyanmar-Bold.otf
+7062395000-7062398000 r-xp 00000000 fc:00 2937 /system/lib64/libjnigraphics.so
+7062398000-70623b4000 ---p 00000000 00:00 0
+70623b4000-70623b5000 r--p 0000f000 fc:00 2937 /system/lib64/libjnigraphics.so
+70623b5000-70623b6000 rw-p 00010000 fc:00 2937 /system/lib64/libjnigraphics.so
+70623c8000-70623e0000 r-xp 00000000 fc:00 2662 /system/lib64/libGLESv3.so
+70623e0000-70623f7000 ---p 00000000 00:00 0
+70623f7000-70623f8000 r--p 0001f000 fc:00 2662 /system/lib64/libGLESv3.so
+70623f8000-70623f9000 rw-p 00020000 fc:00 2662 /system/lib64/libGLESv3.so
+70623fc000-706241c000 rw-p 00000000 00:05 10269729 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+706241c000-7062444000 r-xp 00000000 fc:00 2603 /system/lib64/libexif.so
+7062444000-706245f000 ---p 00000000 00:00 0
+706245f000-7062472000 r--p 0002d000 fc:00 2603 /system/lib64/libexif.so
+7062472000-7062473000 rw-p 00040000 fc:00 2603 /system/lib64/libexif.so
+7062474000-7062490000 r--s 00000000 fc:00 286 /system/fonts/NotoSansMongolian-Regular.ttf
+7062490000-7062491000 r-xp 00000000 fc:00 2357 /system/lib64/libasyncio.so
+7062491000-70624af000 ---p 00000000 00:00 0
+70624af000-70624b0000 r--p 0000f000 fc:00 2357 /system/lib64/libasyncio.so
+70624b0000-70624b1000 rw-p 00010000 fc:00 2357 /system/lib64/libasyncio.so
+70624b5000-70624cf000 r--s 00000000 fc:00 221 /system/fonts/NotoSansMyanmarUI-Bold.ttf
+70624cf000-7062508000 r-xp 00000000 fc:00 2401 /system/lib64/libmtp.so
+7062508000-7062522000 ---p 00000000 00:00 0
+7062522000-7062525000 r--p 0003d000 fc:00 2401 /system/lib64/libmtp.so
+7062525000-706252c000 rw-p 00040000 fc:00 2401 /system/lib64/libmtp.so
+7062530000-7062550000 rw-p 00000000 00:05 10269728 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062550000-7062572000 r--s 00000000 fc:00 234 /system/fonts/NotoSerifMyanmar-Regular.otf
+7062572000-706259e000 r-xp 00000000 fc:00 2620 /system/lib64/libmediandk.so
+706259e000-70625b9000 ---p 00000000 00:00 0
+70625b9000-70625bc000 r--p 0002d000 fc:00 2620 /system/lib64/libmediandk.so
+70625bc000-70625c0000 rw-p 00030000 fc:00 2620 /system/lib64/libmediandk.so
+70625c2000-70625d1000 r-xp 00000000 fc:00 2613 /system/lib64/libmidi.so
+70625d1000-70625ef000 ---p 00000000 00:00 0
+70625ef000-70625f1000 r--p 0000e000 fc:00 2613 /system/lib64/libmidi.so
+70625f1000-70625f2000 rw-p 00010000 fc:00 2613 /system/lib64/libmidi.so
+7062600000-7062621000 r-xp 00000000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+7062621000-706263d000 ---p 00000000 00:00 0
+706263d000-706263f000 r--p 0002e000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+706263f000-7062640000 rw-p 00030000 fc:00 2366 /system/lib64/libmediadrmmetrics_lite.so
+706264b000-706266b000 rw-p 00000000 00:05 10269727 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+706266b000-70626d4000 r-xp 00000000 fc:00 2727 /system/lib64/libmedia_jni.so
+70626d4000-70626eb000 ---p 00000000 00:00 0
+70626eb000-70626f2000 r--p 00069000 fc:00 2727 /system/lib64/libmedia_jni.so
+70626f2000-70626f3000 rw-p 00070000 fc:00 2727 /system/lib64/libmedia_jni.so
+7062703000-7062732000 r-xp 00000000 fc:00 2399 /system/lib64/libcamera2ndk.so
+7062732000-7062748000 ---p 00000000 00:00 0
+7062748000-706274b000 r--p 0003d000 fc:00 2399 /system/lib64/libcamera2ndk.so
+706274b000-7062750000 rw-p 00040000 fc:00 2399 /system/lib64/libcamera2ndk.so
+7062768000-7062788000 rw-p 00000000 00:05 10269726 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062788000-70627ee000 r-xp 00000000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+70627ee000-7062805000 ---p 00000000 00:00 0
+7062805000-706280d000 r--p 00068000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+706280d000-706280e000 rw-p 00070000 fc:00 2974 /system/lib64/android.hardware.drm@1.0.so
+706281a000-706281b000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+706281b000-706281f000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+706281f000-7062843000 r--s 00000000 fc:00 142 /system/fonts/NotoSansKhmer-VF.ttf
+7062843000-7062886000 r-xp 00000000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+7062886000-70628a5000 ---p 00000000 00:00 0
+70628a5000-70628ab000 r--p 0004a000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+70628ab000-70628ac000 rw-p 00050000 fc:00 2637 /system/lib64/android.hardware.drm@1.1.so
+70628b0000-70628b1000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70628b1000-70628b5000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70628b5000-70628db000 r--s 00000000 fc:00 137 /system/fonts/NotoSansSinhala-Bold.ttf
+70628db000-7062907000 r-xp 00000000 fc:00 2478 /system/lib64/libmediadrm.so
+7062907000-7062918000 ---p 00000000 00:00 0
+7062918000-7062920000 r--p 00038000 fc:00 2478 /system/lib64/libmediadrm.so
+7062920000-7062921000 rw-p 00040000 fc:00 2478 /system/lib64/libmediadrm.so
+7062922000-7062929000 rw-p 00000000 fc:00 583 /system/etc/event-log-tags
+7062929000-7062951000 r--s 00000000 fc:00 296 /system/fonts/NotoSansSinhala-Regular.ttf
+7062951000-7062997000 r-xp 00000000 fc:00 2448 /system/lib64/libaaudio.so
+7062997000-70629ac000 ---p 00000000 00:00 0
+70629ac000-70629b2000 r--p 0004a000 fc:00 2448 /system/lib64/libaaudio.so
+70629b2000-70629ba000 rw-p 00050000 fc:00 2448 /system/lib64/libaaudio.so
+70629ba000-70629bb000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70629bb000-70629bf000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70629bf000-70629c0000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629c0000-70629c3000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70629c3000-70629c4000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629c4000-70629c5000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70629c5000-70629c9000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70629c9000-70629e3000 r-xp 00000000 fc:00 2940 /system/lib64/libandroid.so
+70629e3000-70629f3000 ---p 00000000 00:00 0
+70629f3000-70629f6000 r--p 0001d000 fc:00 2940 /system/lib64/libandroid.so
+70629f6000-70629f7000 rw-p 00020000 fc:00 2940 /system/lib64/libandroid.so
+70629f8000-70629f9000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629f9000-70629fc000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70629fc000-70629fd000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70629fd000-7062a3e000 r--s 00000000 fc:00 216 /system/fonts/NotoSerif-BoldItalic.ttf
+7062a3e000-7062b06000 rw-p 00000000 00:05 10270984 /dev/ashmem/dalvik-indirect ref table (deleted)
+7062b06000-7062bce000 rw-p 00000000 00:05 10270983 /dev/ashmem/dalvik-indirect ref table (deleted)
+7062bce000-7062dce000 rw-p 00000000 00:05 10270726 /dev/ashmem/dalvik-rb copying gc mark stack (deleted)
+7062dce000-70635ce000 rw-p 00000000 00:05 10270725 /dev/ashmem/dalvik-concurrent copying gc mark stack (deleted)
+70635ce000-7063dcf000 rw-p 00000000 00:05 10270724 /dev/ashmem/dalvik-live stack (deleted)
+7063dcf000-70645d0000 rw-p 00000000 00:05 10270723 /dev/ashmem/dalvik-allocation stack (deleted)
+70645d0000-70649d1000 rw-p 00000000 00:05 10270721 /dev/ashmem/dalvik-card table (deleted)
+70649d1000-7064ad1000 rw-p 00000000 00:05 10267648 /dev/ashmem/dalvik-large object free list space allocation info map (deleted)
+7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644 /dev/ashmem/dalvik-region space live bitmap (deleted)
+7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642 /dev/ashmem/dalvik-allocspace zygote / non moving space mark-bitmap 0 (deleted)
+7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641 /dev/ashmem/dalvik-allocspace zygote / non moving space live-bitmap 0 (deleted)
+7065cd1000-7065cd2000 r-xp 00000000 fc:00 2946 /system/lib64/libsigchain.so
+7065cd2000-7065cf0000 ---p 00000000 00:00 0
+7065cf0000-7065cf1000 r--p 0000f000 fc:00 2946 /system/lib64/libsigchain.so
+7065cf1000-7065cf2000 rw-p 00010000 fc:00 2946 /system/lib64/libsigchain.so
+7065cf4000-7065d0f000 r--s 00000000 fc:00 190 /system/fonts/NotoSansMyanmar-Bold.ttf
+7065d0f000-7065d22000 r-xp 00000000 fc:00 2405 /system/lib64/liblz4.so
+7065d22000-7065d3e000 ---p 00000000 00:00 0
+7065d3e000-7065d3f000 r--p 0001f000 fc:00 2405 /system/lib64/liblz4.so
+7065d3f000-7065d40000 rw-p 00020000 fc:00 2405 /system/lib64/liblz4.so
+7065d40000-7065d5a000 r--s 00000000 fc:00 222 /system/fonts/NotoSansMyanmarUI-Regular.ttf
+7065d5a000-7065d5e000 r-xp 00000000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d5e000-7065d79000 ---p 00000000 00:00 0
+7065d79000-7065d7a000 r--p 0000f000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d7a000-7065d7b000 rw-p 00010000 fc:00 2609 /system/lib64/libtombstoned_client.so
+7065d7f000-7065d80000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+7065d80000-7065d84000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+7065d84000-706636e000 r-xp 00000000 fc:00 2671 /system/lib64/libart.so
+706636e000-706638d000 ---p 00000000 00:00 0
+706638d000-706639e000 r--p 005ef000 fc:00 2671 /system/lib64/libart.so
+706639e000-70663a1000 rw-p 00600000 fc:00 2671 /system/lib64/libart.so
+70663a1000-70663a4000 rw-p 00000000 00:00 0 [anon:.bss]
+70663a6000-70663c6000 rw-p 00000000 00:05 10269725 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70663c6000-70663c8000 r-xp 00000000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663c8000-70663e5000 ---p 00000000 00:00 0
+70663e5000-70663e6000 r--p 0000f000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663e6000-70663e7000 rw-p 00010000 fc:00 2673 /system/lib64/libmetricslogger.so
+70663e7000-7066400000 r--s 00000000 fc:00 110 /system/fonts/NotoSansLepcha-Regular.ttf
+7066400000-7066800000 rw-p 00000000 00:00 0 [anon:libc_malloc]
+7066803000-706681e000 r--s 00000000 fc:00 297 /system/fonts/NotoSansMyanmar-Regular.ttf
+706681e000-7066821000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066821000-7066822000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066822000-7066b1d000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066b1d000-7066b1e000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066b1e000-7066ba0000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba0000-7066ba1000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba1000-7066ba2000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba2000-7066ba5000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba5000-7066ba6000 r--p 00000000 00:00 0 [anon:cfi shadow]
+7066ba6000-70e681e000 r--p 00000000 00:00 0 [anon:cfi shadow]
+70e681e000-70e6854000 r-xp 00000000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e6854000-70e6865000 ---p 00000000 00:00 0
+70e6865000-70e6867000 r--p 0003e000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e6867000-70e686c000 rw-p 00040000 fc:00 2431 /system/lib64/libstagefright_foundation.so
+70e686d000-70e686e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e686e000-70e6871000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6871000-70e6873000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6873000-70e6876000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6876000-70e6877000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6877000-70e688c000 r--s 00000000 fc:00 301 /system/fonts/NotoSansSinhalaUI-Bold.otf
+70e688c000-70e688e000 r-xp 00000000 fc:00 2943 /system/lib64/libion.so
+70e688e000-70e68ab000 ---p 00000000 00:00 0
+70e68ab000-70e68ac000 r--p 0000f000 fc:00 2943 /system/lib64/libion.so
+70e68ac000-70e68ad000 rw-p 00010000 fc:00 2943 /system/lib64/libion.so
+70e68ad000-70e68af000 rw-p 00000000 00:05 10282496 /dev/ashmem/dalvik-indirect ref table (deleted)
+70e68af000-70e68b1000 rw-p 00000000 00:05 10282493 /dev/ashmem/dalvik-indirect ref table (deleted)
+70e68b1000-70e68ee000 r--s 00000000 fc:00 256 /system/fonts/NotoSerif-Italic.ttf
+70e68ee000-70e6910000 r-xp 00000000 fc:00 2502 /system/lib64/libhidlbase.so
+70e6910000-70e692c000 ---p 00000000 00:00 0
+70e692c000-70e692e000 r--p 0002e000 fc:00 2502 /system/lib64/libhidlbase.so
+70e692e000-70e692f000 rw-p 00030000 fc:00 2502 /system/lib64/libhidlbase.so
+70e6930000-70e693f000 r--s 00000000 fc:00 1082 /system/usr/hyphen-data/hyph-en-us.hyb
+70e693f000-70e6954000 r--s 00000000 fc:00 138 /system/fonts/NotoSansSinhalaUI-Regular.otf
+70e6954000-70e6978000 r-xp 00000000 fc:00 2482 /system/lib64/libui.so
+70e6978000-70e6992000 ---p 00000000 00:00 0
+70e6992000-70e6994000 r--p 0002e000 fc:00 2482 /system/lib64/libui.so
+70e6994000-70e6995000 rw-p 00030000 fc:00 2482 /system/lib64/libui.so
+70e6996000-70e69a2000 r--s 00000000 fc:00 1117 /system/usr/hyphen-data/hyph-en-gb.hyb
+70e69a2000-70e69b7000 r--s 00000000 fc:00 202 /system/fonts/NotoSerifSinhala-Bold.otf
+70e69b7000-70e69cb000 r--s 00000000 fc:00 124 /system/fonts/NotoSansOriyaUI-Bold.ttf
+70e69cb000-70e69e1000 r-xp 00000000 fc:00 2537 /system/lib64/liblog.so
+70e69e1000-70e69fa000 ---p 00000000 00:00 0
+70e69fa000-70e69fb000 r--p 0001f000 fc:00 2537 /system/lib64/liblog.so
+70e69fb000-70e69fc000 rw-p 00020000 fc:00 2537 /system/lib64/liblog.so
+70e69fc000-70e69fe000 rw-p 00000000 00:05 10266158 /dev/ashmem/dalvik-indirect ref table (deleted)
+70e69fe000-70e69ff000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e69ff000-70e6a02000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6a02000-70e6a03000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6a03000-70e6a05000 r-xp 00000000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a05000-70e6a22000 ---p 00000000 00:00 0
+70e6a22000-70e6a23000 r--p 0000f000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a23000-70e6a24000 rw-p 00010000 fc:00 2489 /system/lib64/android.hidl.token@1.0-utils.so
+70e6a25000-70e6a2e000 r--s 00000000 fc:00 1120 /system/usr/hyphen-data/hyph-ga.hyb
+70e6a2e000-70e6a42000 r--s 00000000 fc:00 109 /system/fonts/NotoSansOriyaUI-Regular.ttf
+70e6a42000-70e6a59000 r-xp 00000000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a59000-70e6a6e000 ---p 00000000 00:00 0
+70e6a6e000-70e6a70000 r--p 0001e000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a70000-70e6a71000 rw-p 00020000 fc:00 2446 /system/lib64/android.hardware.graphics.mapper@2.0.so
+70e6a72000-70e6a78000 r--s 00000000 fc:00 1084 /system/usr/hyphen-data/hyph-et.hyb
+70e6a78000-70e6a9d000 r--s 00000000 fc:00 207 /system/fonts/NotoSerifTelugu-Bold.ttf
+70e6a9d000-70e6a9f000 r-xp 00000000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6a9f000-70e6abc000 ---p 00000000 00:00 0
+70e6abc000-70e6abd000 r--p 0000f000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6abd000-70e6abe000 rw-p 00010000 fc:00 2330 /system/lib64/android.hardware.configstore-utils.so
+70e6abe000-70e6ac0000 r--s f8042000 00:10 20630 /dev/kgsl-3d0
+70e6ac0000-70e6adc000 r--s 00000000 fc:00 172 /system/fonts/NotoSansTeluguUI-Bold.ttf
+70e6adc000-70e6ae0000 r-xp 00000000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6ae0000-70e6afb000 ---p 00000000 00:00 0
+70e6afb000-70e6afc000 r--p 0000f000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6afc000-70e6afd000 rw-p 00010000 fc:00 2555 /system/lib64/libstagefright_omx_utils.so
+70e6afd000-70e6afe000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70e6afe000-70e6b02000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70e6b02000-70e6b27000 r--s 00000000 fc:00 271 /system/fonts/NotoSerifTelugu-Regular.ttf
+70e6b27000-70e6b61000 r-xp 00000000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b61000-70e6b73000 ---p 00000000 00:00 0
+70e6b73000-70e6b75000 r--p 0003e000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b75000-70e6b76000 rw-p 00040000 fc:00 2695 /system/lib64/libdexfile.so
+70e6b76000-70e6b78000 rw-p 00000000 00:05 10253452 /dev/ashmem/dalvik-indirect ref table (deleted)
+70e6b78000-70e6b85000 r--s 00000000 fc:00 1080 /system/usr/hyphen-data/hyph-cu.hyb
+70e6b85000-70e6b96000 r-xp 00000000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6b96000-70e6bb4000 ---p 00000000 00:00 0
+70e6bb4000-70e6bb5000 r--p 0001f000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6bb5000-70e6bb6000 rw-p 00020000 fc:00 2957 /system/lib64/libaudioutils.so
+70e6bb6000-70e6bb7000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6bb7000-70e6bba000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70e6bba000-70e6bbb000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70e6bbb000-70e6bd7000 r--s 00000000 fc:00 132 /system/fonts/NotoSansTeluguUI-Regular.ttf
+70e6bd7000-70e6bdc000 r-xp 00000000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bdc000-70e6bf6000 ---p 00000000 00:00 0
+70e6bf6000-70e6bf7000 r--p 0000f000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bf7000-70e6bf8000 rw-p 00010000 fc:00 2409 /system/lib64/libprocessgroup.so
+70e6bf8000-70e6c09000 r--s 00000000 fc:00 79 /system/fonts/NotoSansNewa-Regular.otf
+70e6c09000-70e6c1c000 r-xp 00000000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c1c000-70e6c36000 ---p 00000000 00:00 0
+70e6c36000-70e6c38000 r--p 0001e000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c38000-70e6c39000 rw-p 00020000 fc:00 2329 /system/lib64/android.hidl.memory.token@1.0.so
+70e6c3a000-70e6c4f000 r--s 00000000 fc:00 253 /system/fonts/NotoSansOriya-Bold.ttf
+70e6c4f000-70e6c6b000 r-xp 00000000 fc:00 2407 /system/lib64/libutils.so
+70e6c6b000-70e6c7e000 ---p 00000000 00:00 0
+70e6c7e000-70e6c7f000 r--p 0001f000 fc:00 2407 /system/lib64/libutils.so
+70e6c7f000-70e6c80000 rw-p 00020000 fc:00 2407 /system/lib64/libutils.so
+70e6c80000-70e6c9d000 r-xp 00000000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6c9d000-70e6cba000 ---p 00000000 00:00 0
+70e6cba000-70e6cbc000 r--p 0001e000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6cbc000-70e6cbf000 rw-p 00020000 fc:00 2934 /system/lib64/libtinyxml2.so
+70e6cbf000-70e6ccf000 r--s 00000000 fc:00 80 /system/fonts/NotoSansMarchen-Regular.otf
+70e6ccf000-70e6ce0000 r-xp 00000000 fc:00 2655 /system/lib64/libbase.so
+70e6ce0000-70e6cfe000 ---p 00000000 00:00 0
+70e6cfe000-70e6cff000 r--p 0001f000 fc:00 2655 /system/lib64/libbase.so
+70e6cff000-70e6d00000 rw-p 00020000 fc:00 2655 /system/lib64/libbase.so
+70e6d00000-70e6d09000 r--s 00000000 fc:00 1113 /system/usr/hyphen-data/hyph-cy.hyb
+70e6d09000-70e6d50000 r-xp 00000000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d50000-70e6d68000 ---p 00000000 00:00 0
+70e6d68000-70e6d69000 r--p 0004f000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d69000-70e6d6a000 rw-p 00050000 fc:00 2495 /system/lib64/libRScpp.so
+70e6d6b000-70e6d6d000 r--s 00088000 103:1d 1736830 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e6d6d000-70e6d7d000 r--s 00000000 fc:00 238 /system/fonts/NotoSansVai-Regular.ttf
+70e6d7d000-70e6d98000 r--s 00000000 fc:00 276 /system/fonts/NotoSansTelugu-Bold.ttf
+70e6d98000-70e6f2b000 r-xp 00000000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f2b000-70e6f47000 ---p 00000000 00:00 0
+70e6f47000-70e6f5c000 r--p 0019b000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f5c000-70e6f5d000 rw-p 001b0000 fc:00 2961 /system/lib64/libicuuc.so
+70e6f5d000-70e6f5e000 rw-p 00000000 00:00 0 [anon:.bss]
+70e6f5f000-70e6f68000 r--s 00000000 fc:00 159 /system/fonts/NotoSansLinearA-Regular.otf
+70e6f68000-70e6f84000 r--s 00000000 fc:00 170 /system/fonts/NotoSansTelugu-Regular.ttf
+70e6f84000-70e7058000 r-xp 00000000 fc:00 2356 /system/lib64/libc.so
+70e7058000-70e706e000 ---p 00000000 00:00 0
+70e706e000-70e7074000 r--p 000da000 fc:00 2356 /system/lib64/libc.so
+70e7074000-70e7076000 rw-p 000e0000 fc:00 2356 /system/lib64/libc.so
+70e7076000-70e7077000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7077000-70e7078000 r--p 00000000 00:00 0 [anon:.bss]
+70e7078000-70e7080000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7080000-70e7087000 r--s 00000000 fc:00 102 /system/fonts/NotoSansSharada-Regular.otf
+70e7087000-70e708e000 r-xp 00000000 fc:00 2378 /system/lib64/libheif.so
+70e708e000-70e70a4000 ---p 00000000 00:00 0
+70e70a4000-70e70a6000 r--p 0000e000 fc:00 2378 /system/lib64/libheif.so
+70e70a6000-70e70a7000 rw-p 00010000 fc:00 2378 /system/lib64/libheif.so
+70e70a7000-70e70a9000 r--s 00000000 fc:00 1116 /system/usr/hyphen-data/hyph-sl.hyb
+70e70a9000-70e70ab000 r--s 00000000 fc:00 1147 /system/usr/hyphen-data/hyph-mn-cyrl.hyb
+70e70ab000-70e70c5000 r--s 00000000 fc:00 291 /system/fonts/NotoSansBengaliUI-Bold.ttf
+70e70c5000-70e70ea000 r-xp 00000000 fc:00 2545 /system/lib64/libEGL.so
+70e70ea000-70e7109000 ---p 00000000 00:00 0
+70e7109000-70e710d000 r--p 0002c000 fc:00 2545 /system/lib64/libEGL.so
+70e710d000-70e710e000 rw-p 00030000 fc:00 2545 /system/lib64/libEGL.so
+70e710e000-70e7115000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7115000-70e7119000 r--s 00000000 fc:00 1143 /system/usr/hyphen-data/hyph-es.hyb
+70e7119000-70e712e000 r--s 00000000 fc:00 71 /system/fonts/NotoSansOriya-Regular.ttf
+70e712e000-70e717a000 r--s 00000000 fc:00 272 /system/fonts/Roboto-Thin.ttf
+70e717a000-70e71db000 r-xp 00000000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71db000-70e71f7000 ---p 00000000 00:00 0
+70e71f7000-70e71f9000 r--p 0006e000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71f9000-70e71fa000 rw-p 00070000 fc:00 2393 /system/lib64/libpdx_default_transport.so
+70e71fa000-70e71fb000 rw-p 00000000 00:00 0 [anon:.bss]
+70e71fc000-70e71fe000 r--s 00000000 fc:00 1107 /system/usr/hyphen-data/hyph-fr.hyb
+70e71fe000-70e7200000 r--s 00000000 fc:00 1097 /system/usr/hyphen-data/hyph-da.hyb
+70e7200000-70e7223000 r-xp 00000000 fc:00 2380 /system/lib64/libminikin.so
+70e7223000-70e723e000 ---p 00000000 00:00 0
+70e723e000-70e723f000 r--p 0002f000 fc:00 2380 /system/lib64/libminikin.so
+70e723f000-70e7240000 rw-p 00030000 fc:00 2380 /system/lib64/libminikin.so
+70e7241000-70e724d000 r--s 00000000 fc:00 179 /system/fonts/NotoSansTaiTham-Regular.ttf
+70e724d000-70e725c000 r-xp 00000000 fc:00 2527 /system/lib64/libmediautils.so
+70e725c000-70e7279000 ---p 00000000 00:00 0
+70e7279000-70e727b000 r--p 0001e000 fc:00 2527 /system/lib64/libmediautils.so
+70e727b000-70e727c000 rw-p 00020000 fc:00 2527 /system/lib64/libmediautils.so
+70e727d000-70e7283000 r--s 00000000 fc:00 136 /system/fonts/NotoSansMiao-Regular.otf
+70e7283000-70e74d2000 r-xp 00000000 fc:00 2349 /system/lib64/libicui18n.so
+70e74d2000-70e74e6000 ---p 00000000 00:00 0
+70e74e6000-70e74fa000 r--p 0025c000 fc:00 2349 /system/lib64/libicui18n.so
+70e74fa000-70e74fb000 rw-p 00270000 fc:00 2349 /system/lib64/libicui18n.so
+70e74fc000-70e74ff000 r--s 00000000 103:1d 1474562 /data/resource-cache/vendor@overlay@framework-res__auto_generated_rro.apk@idmap
+70e74ff000-70e750c000 r--s 00000000 fc:00 126 /system/fonts/NotoSansSyriacWestern-Regular.ttf
+70e750c000-70e751b000 r-xp 00000000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e751b000-70e753a000 ---p 00000000 00:00 0
+70e753a000-70e753b000 r--p 0000f000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e753b000-70e753c000 rw-p 00010000 fc:00 2466 /system/lib64/libmediaextractor.so
+70e753d000-70e7540000 r--s 00000000 fc:00 107 /system/fonts/NotoSansPauCinHau-Regular.otf
+70e7540000-70e7593000 r-xp 00000000 fc:00 2363 /system/lib64/libandroidfw.so
+70e7593000-70e75ac000 ---p 00000000 00:00 0
+70e75ac000-70e75af000 r--p 0005d000 fc:00 2363 /system/lib64/libandroidfw.so
+70e75af000-70e75b0000 rw-p 00060000 fc:00 2363 /system/lib64/libandroidfw.so
+70e75b0000-70e75b2000 r--s 00000000 fc:00 1083 /system/usr/hyphen-data/hyph-be.hyb
+70e75b2000-70e75cd000 r--s 00000000 fc:00 270 /system/fonts/NotoSansBengaliUI-Regular.ttf
+70e75cd000-70e75cf000 r-xp 00000000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75cf000-70e75ec000 ---p 00000000 00:00 0
+70e75ec000-70e75ed000 r--p 0000f000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75ed000-70e75ee000 rw-p 00010000 fc:00 2701 /system/lib64/libmemtrack.so
+70e75ee000-70e75f0000 r--s 00000000 fc:00 209 /system/fonts/NotoSansSoraSompeng-Regular.otf
+70e75f0000-70e760d000 r--s 00000000 fc:00 243 /system/fonts/NotoSerifBengali-Bold.ttf
+70e760d000-70e7613000 r-xp 00000000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e7613000-70e762c000 ---p 00000000 00:00 0
+70e762c000-70e762d000 r--p 0000f000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e762d000-70e762e000 rw-p 00010000 fc:00 2667 /system/lib64/libutilscallstack.so
+70e762e000-70e7632000 r--s 00000000 fc:00 99 /system/fonts/NotoSansPahawhHmong-Regular.otf
+70e7632000-70e764f000 r--s 00000000 fc:00 205 /system/fonts/NotoSerifBengali-Regular.ttf
+70e764f000-70e7661000 r-xp 00000000 fc:00 2710 /system/lib64/libcutils.so
+70e7661000-70e767d000 ---p 00000000 00:00 0
+70e767d000-70e767f000 r--p 0001e000 fc:00 2710 /system/lib64/libcutils.so
+70e767f000-70e7680000 rw-p 00020000 fc:00 2710 /system/lib64/libcutils.so
+70e7680000-70e7683000 r--s 00000000 fc:00 257 /system/fonts/NotoSansPalmyrene-Regular.otf
+70e7683000-70e7697000 r--s 00000000 fc:00 78 /system/fonts/NotoSansKannadaUI-Bold.ttf
+70e7697000-70e769a000 r-xp 00000000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e769a000-70e76b6000 ---p 00000000 00:00 0
+70e76b6000-70e76b7000 r--p 0000f000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e76b7000-70e76b8000 rw-p 00010000 fc:00 2362 /system/lib64/libstagefright_http_support.so
+70e76b8000-70e76b9000 rw-p 00000000 00:00 0 [anon:linker_alloc_lob]
+70e76b9000-70e76be000 r--s 00000000 fc:00 165 /system/fonts/NotoSansMeroitic-Regular.otf
+70e76be000-70e76cb000 r--s 00000000 fc:00 112 /system/fonts/NotoSansSyriacEastern-Regular.ttf
+70e76cb000-70e76de000 r-xp 00000000 fc:00 2343 /system/lib64/libsensor.so
+70e76de000-70e76f5000 ---p 00000000 00:00 0
+70e76f5000-70e76f8000 r--p 0001d000 fc:00 2343 /system/lib64/libsensor.so
+70e76f8000-70e76f9000 rw-p 00020000 fc:00 2343 /system/lib64/libsensor.so
+70e76f9000-70e76fc000 r--s 00000000 fc:00 157 /system/fonts/NotoSansOldPermic-Regular.otf
+70e76fc000-70e7708000 r--s 00000000 fc:00 189 /system/fonts/NotoSansSyriacEstrangela-Regular.ttf
+70e7708000-70e771d000 r-xp 00000000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e771d000-70e7735000 ---p 00000000 00:00 0
+70e7735000-70e7737000 r--p 0001e000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e7737000-70e7738000 rw-p 00020000 fc:00 2339 /system/lib64/android.hidl.token@1.0.so
+70e7738000-70e7739000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e7739000-70e773b000 r--s 00000000 fc:00 267 /system/fonts/NotoSansOldNorthArabian-Regular.otf
+70e773b000-70e7740000 r--s 00000000 fc:00 208 /system/fonts/NotoSansManichaean-Regular.otf
+70e7740000-70e775c000 r--s 00000000 fc:00 118 /system/fonts/NotoSansGujaratiUI-Bold.ttf
+70e775c000-70e7767000 r-xp 00000000 fc:00 2525 /system/lib64/libappfuse.so
+70e7767000-70e777b000 ---p 00000000 00:00 0
+70e777b000-70e777c000 r--p 0000f000 fc:00 2525 /system/lib64/libappfuse.so
+70e777c000-70e777d000 rw-p 00010000 fc:00 2525 /system/lib64/libappfuse.so
+70e777e000-70e7795000 r--s 00000000 fc:00 250 /system/fonts/NotoSerifKannada-Bold.ttf
+70e7795000-70e7798000 r-xp 00000000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e7798000-70e77b4000 ---p 00000000 00:00 0
+70e77b4000-70e77b5000 r--p 0000f000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e77b5000-70e77b6000 rw-p 00010000 fc:00 2413 /system/lib64/libpackagelistparser.so
+70e77b6000-70e77b8000 r--s 00000000 fc:00 147 /system/fonts/NotoSansNabataean-Regular.otf
+70e77b8000-70e77ba000 r--s 00000000 fc:00 146 /system/fonts/NotoSansMultani-Regular.otf
+70e77ba000-70e77c1000 r--s 00000000 fc:00 214 /system/fonts/NotoSansPhagsPa-Regular.ttf
+70e77c1000-70e77de000 r--s 00000000 fc:00 90 /system/fonts/NotoSansGujaratiUI-Regular.ttf
+70e77de000-70e77e0000 r-xp 00000000 fc:00 2430 /system/lib64/libdl.so
+70e77e0000-70e77fd000 ---p 00000000 00:00 0
+70e77fd000-70e77fe000 r--p 0000f000 fc:00 2430 /system/lib64/libdl.so
+70e77fe000-70e77ff000 r--p 00000000 00:00 0 [anon:.bss]
+70e7800000-70e7809000 r--s 00000000 fc:00 258 /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf
+70e7809000-70e7857000 r-xp 00000000 fc:00 2560 /system/lib64/libjpeg.so
+70e7857000-70e7868000 ---p 00000000 00:00 0
+70e7868000-70e7869000 r--p 0004f000 fc:00 2560 /system/lib64/libjpeg.so
+70e7869000-70e786a000 rw-p 00050000 fc:00 2560 /system/lib64/libjpeg.so
+70e786a000-70e7887000 r--s 00000000 fc:00 237 /system/fonts/NotoSansGujarati-Bold.ttf
+70e7887000-70e788a000 r-xp 00000000 fc:00 2608 /system/lib64/libnetd_client.so
+70e788a000-70e78a6000 ---p 00000000 00:00 0
+70e78a6000-70e78a7000 r--p 0000f000 fc:00 2608 /system/lib64/libnetd_client.so
+70e78a7000-70e78a8000 rw-p 00010000 fc:00 2608 /system/lib64/libnetd_client.so
+70e78a8000-70e78aa000 r--s 00000000 fc:00 290 /system/fonts/NotoSansMro-Regular.otf
+70e78aa000-70e78b9000 r--s 00000000 fc:00 84 /system/fonts/NotoSansLinearB-Regular.ttf
+70e78b9000-70e78d7000 r--s 00000000 fc:00 287 /system/fonts/NotoSansGujarati-Regular.ttf
+70e78d7000-70e78d8000 r-xp 00000000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78d8000-70e78f6000 ---p 00000000 00:00 0
+70e78f6000-70e78f7000 r--p 0000f000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78f7000-70e78f8000 rw-p 00010000 fc:00 2651 /system/lib64/libvndksupport.so
+70e78f9000-70e78fc000 r--s 00000000 fc:00 178 /system/fonts/NotoSansTaiLe-Regular.ttf
+70e78fc000-70e7900000 r--s 00000000 fc:00 163 /system/fonts/NotoSansTifinagh-Regular.ttf
+70e7900000-70e7906000 r-xp 00000000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7906000-70e791f000 ---p 00000000 00:00 0
+70e791f000-70e7920000 r--p 0000f000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7920000-70e7921000 rw-p 00010000 fc:00 2659 /system/lib64/libnativeloader.so
+70e7921000-70e7923000 r--s 00000000 fc:00 268 /system/fonts/NotoSansHatran-Regular.otf
+70e7923000-70e7927000 r--s 00000000 fc:00 195 /system/fonts/NotoSansTaiViet-Regular.ttf
+70e7927000-70e7945000 r--s 00000000 fc:00 139 /system/fonts/NotoSansDevanagariUI-Bold.ttf
+70e7945000-70e79a3000 r-xp 00000000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79a3000-70e79b3000 ---p 00000000 00:00 0
+70e79b3000-70e79b5000 r--p 0005e000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79b5000-70e79b6000 rw-p 00060000 fc:00 2450 /system/lib64/libharfbuzz_ng.so
+70e79b6000-70e79c5000 r--s 00000000 fc:00 111 /system/fonts/NotoSansKaithi-Regular.ttf
+70e79c5000-70e7a92000 r-xp 00000000 fc:00 2332 /system/lib64/libc++.so
+70e7a92000-70e7aae000 ---p 00000000 00:00 0
+70e7aae000-70e7ab8000 r--p 000d6000 fc:00 2332 /system/lib64/libc++.so
+70e7ab8000-70e7ab9000 rw-p 000e0000 fc:00 2332 /system/lib64/libc++.so
+70e7ab9000-70e7abc000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7abc000-70e7abe000 r--s 00000000 fc:00 73 /system/fonts/NotoSansBassaVah-Regular.otf
+70e7abe000-70e7ac8000 r--s 00000000 fc:00 254 /system/fonts/NotoSansJavanese-Regular.ttf
+70e7ac8000-70e7acb000 r-xp 00000000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7acb000-70e7ae7000 ---p 00000000 00:00 0
+70e7ae7000-70e7ae8000 r--p 0000f000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7ae8000-70e7ae9000 rw-p 00010000 fc:00 2660 /system/lib64/libnativebridge.so
+70e7ae9000-70e7aee000 r--s 00000000 fc:00 158 /system/fonts/NotoSansSaurashtra-Regular.ttf
+70e7aee000-70e7b0f000 r--s 00000000 fc:00 82 /system/fonts/NotoSansDevanagari-Bold.ttf
+70e7b0f000-70e7b2e000 r-xp 00000000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b2e000-70e7b3e000 ---p 00000000 00:00 0
+70e7b3e000-70e7b3f000 r--p 0001f000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b3f000-70e7b40000 rw-p 00020000 fc:00 2612 /system/lib64/libpcre2.so
+70e7b40000-70e7bcc000 r-xp 00000000 fc:00 2975 /system/lib64/libgui.so
+70e7bcc000-70e7be2000 ---p 00000000 00:00 0
+70e7be2000-70e7bf5000 r--p 0008d000 fc:00 2975 /system/lib64/libgui.so
+70e7bf5000-70e7bf6000 rw-p 000a0000 fc:00 2975 /system/lib64/libgui.so
+70e7bf6000-70e7bf7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e7bf7000-70e7bf9000 r--s 00000000 fc:00 228 /system/fonts/NotoSansUgaritic-Regular.ttf
+70e7bf9000-70e7c08000 r--s 00000000 fc:00 89 /system/fonts/NotoSansCherokee-Regular.ttf
+70e7c08000-70e7dea000 r-xp 00000000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7dea000-70e7e04000 ---p 00000000 00:00 0
+70e7e04000-70e7e23000 r--p 001e1000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7e23000-70e7e24000 rw-p 00200000 fc:00 2441 /system/lib64/libandroid_runtime.so
+70e7e24000-70e7e28000 rw-p 00000000 00:00 0 [anon:.bss]
+70e7e29000-70e7e66000 r--s 00000000 fc:00 74 /system/fonts/NotoSerif-Bold.ttf
+70e7e66000-70e7ed0000 r-xp 00000000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ed0000-70e7edf000 ---p 00000000 00:00 0
+70e7edf000-70e7ee1000 r--p 00069000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee1000-70e7ee4000 rw-p 0006b000 fc:00 2547 /system/lib64/libclang_rt.ubsan_standalone-aarch64-android.so
+70e7ee4000-70e89f6000 rw-p 00000000 00:00 0 [anon:.bss]
+70e89f6000-70e89fa000 r--s 00000000 fc:00 127 /system/fonts/NotoSansSylotiNagri-Regular.ttf
+70e89fa000-70e8a06000 r--s 00000000 fc:00 93 /system/fonts/NotoSansCanadianAboriginal-Regular.ttf
+70e8a06000-70e8a1b000 r-xp 00000000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a1b000-70e8a33000 ---p 00000000 00:00 0
+70e8a33000-70e8a35000 r--p 0001e000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a35000-70e8a36000 rw-p 00020000 fc:00 2460 /system/lib64/android.hardware.graphics.allocator@2.0.so
+70e8a36000-70e8a55000 r--s 00000000 fc:00 145 /system/fonts/NotoSansDevanagariUI-Regular.ttf
+70e8a55000-70e8a61000 r-xp 00000000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a61000-70e8a74000 ---p 00000000 00:00 0
+70e8a74000-70e8a75000 r--p 0000f000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a75000-70e8a76000 rw-p 00010000 fc:00 2540 /system/lib64/libstagefright_xmlparser.so
+70e8a76000-70e8a78000 r--s 00000000 fc:00 293 /system/fonts/NotoSansTagbanwa-Regular.ttf
+70e8a78000-70e8a8f000 r--s 00000000 fc:00 161 /system/fonts/NotoSerifKannada-Regular.ttf
+70e8a8f000-70e8b23000 r-xp 00000000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b23000-70e8b37000 ---p 00000000 00:00 0
+70e8b37000-70e8b49000 r--p 0009e000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b49000-70e8b55000 rw-p 000b0000 fc:00 2633 /system/lib64/libaudioclient.so
+70e8b55000-70e8b9f000 r--s 00000000 fc:00 83 /system/fonts/RobotoCondensed-Light.ttf
+70e8b9f000-70e8ba1000 r-xp 00000000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8ba1000-70e8bbe000 ---p 00000000 00:00 0
+70e8bbe000-70e8bbf000 r--p 0000f000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8bbf000-70e8bc0000 rw-p 00010000 fc:00 2520 /system/lib64/libhardware_legacy.so
+70e8bc0000-70e8be0000 r-xp 00000000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8be0000-70e8bfa000 ---p 00000000 00:00 0
+70e8bfa000-70e8bfd000 r--p 0002d000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8bfd000-70e8bfe000 rw-p 00030000 fc:00 2410 /system/lib64/android.hidl.memory@1.0.so
+70e8bfe000-70e8bff000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e8bff000-70e8c02000 r--s 00000000 fc:00 273 /system/fonts/NotoSansSundanese-Regular.ttf
+70e8c02000-70e8c0f000 r--s 00000000 fc:00 115 /system/fonts/NotoSansAdlam-Regular.ttf
+70e8c0f000-70e8c18000 r-xp 00000000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c18000-70e8c2e000 ---p 00000000 00:00 0
+70e8c2e000-70e8c2f000 r--p 0000f000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c2f000-70e8c30000 rw-p 00010000 fc:00 2350 /system/lib64/libnetdutils.so
+70e8c30000-70e8c44000 r--s 00000000 fc:00 283 /system/fonts/NotoSansKannadaUI-Regular.ttf
+70e8c44000-70e8c45000 r-xp 00000000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c45000-70e8c63000 ---p 00000000 00:00 0
+70e8c63000-70e8c64000 r--p 0000f000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c64000-70e8c65000 rw-p 00010000 fc:00 2926 /system/lib64/libhidlallocatorutils.so
+70e8c65000-70e8c67000 r--s 00000000 fc:00 65 /system/fonts/NotoSansTagalog-Regular.ttf
+70e8c67000-70e8c70000 r--s 00000000 fc:00 294 /system/fonts/NotoSansChakma-Regular.ttf
+70e8c70000-70e8c92000 r--s 00000000 fc:00 116 /system/fonts/NotoSansDevanagari-Regular.ttf
+70e8c92000-70e8c94000 r-xp 00000000 fc:00 2501 /system/lib64/libsync.so
+70e8c94000-70e8cb1000 ---p 00000000 00:00 0
+70e8cb1000-70e8cb2000 r--p 0000f000 fc:00 2501 /system/lib64/libsync.so
+70e8cb2000-70e8cb3000 rw-p 00010000 fc:00 2501 /system/lib64/libsync.so
+70e8cb3000-70e8cc6000 r--s 00000000 fc:00 196 /system/fonts/NotoSerifSinhala-Regular.otf
+70e8cc6000-70e8d5c000 r-xp 00000000 fc:00 2403 /system/lib64/libmedia.so
+70e8d5c000-70e8d70000 ---p 00000000 00:00 0
+70e8d70000-70e8d88000 r--p 00098000 fc:00 2403 /system/lib64/libmedia.so
+70e8d88000-70e8d95000 rw-p 000b0000 fc:00 2403 /system/lib64/libmedia.so
+70e8d95000-70e8d96000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70e8d96000-70e8d99000 r--s 00000000 fc:00 247 /system/fonts/NotoSansSamaritan-Regular.ttf
+70e8d99000-70e8dad000 r--s 00000000 fc:00 108 /system/fonts/NotoSansKannada-Bold.ttf
+70e8dad000-70e8dcd000 r--s 00000000 fc:00 303 /system/fonts/NotoSerifEthiopic-Bold.otf
+70e8dcd000-70e8de5000 r-xp 00000000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8de5000-70e8dfc000 ---p 00000000 00:00 0
+70e8dfc000-70e8dfd000 r--p 0001f000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8dfd000-70e8dfe000 rw-p 00020000 fc:00 2954 /system/lib64/libGLESv2.so
+70e8dfe000-70e8e06000 r--s 00000000 fc:00 265 /system/fonts/NotoSansBalinese-Regular.ttf
+70e8e06000-70e8e0e000 r--s 00000000 fc:00 219 /system/fonts/NotoSansLaoUI-Bold.ttf
+70e8e0e000-70e8e12000 r-xp 00000000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e12000-70e8e2d000 ---p 00000000 00:00 0
+70e8e2d000-70e8e2e000 r--p 0000f000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e2e000-70e8e2f000 rw-p 00010000 fc:00 2617 /system/lib64/libdebuggerd_client.so
+70e8e2f000-70e8e4b000 r--s 00000000 fc:00 211 /system/fonts/NotoSerifEthiopic-Regular.otf
+70e8e4b000-70e8e5e000 r-xp 00000000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e5e000-70e8e78000 ---p 00000000 00:00 0
+70e8e78000-70e8e7a000 r--p 0001e000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7a000-70e8e7b000 rw-p 00020000 fc:00 2484 /system/lib64/android.hardware.memtrack@1.0.so
+70e8e7b000-70e8e7d000 r--s 00000000 fc:00 261 /system/fonts/NotoSansShavian-Regular.ttf
+70e8e7d000-70e8e80000 r--s 00000000 fc:00 204 /system/fonts/NotoSansRunic-Regular.ttf
+70e8e80000-70e8ea1000 r-xp 00000000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ea1000-70e8ebb000 ---p 00000000 00:00 0
+70e8ebb000-70e8ebe000 r--p 0002d000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ebe000-70e8ebf000 rw-p 00030000 fc:00 2512 /system/lib64/android.hardware.configstore@1.0.so
+70e8ebf000-70e8ee3000 r--s 00000000 fc:00 226 /system/fonts/NotoSansEthiopic-Bold.ttf
+70e8ee3000-70e8f1a000 r-xp 00000000 fc:00 2337 /system/lib64/libm.so
+70e8f1a000-70e8f32000 ---p 00000000 00:00 0
+70e8f32000-70e8f33000 r--p 0003f000 fc:00 2337 /system/lib64/libm.so
+70e8f33000-70e8f34000 rw-p 00040000 fc:00 2337 /system/lib64/libm.so
+70e8f34000-70e8f36000 r--s 00000000 fc:00 133 /system/fonts/NotoSansRejang-Regular.ttf
+70e8f36000-70e8f38000 r--s 00000000 fc:00 69 /system/fonts/NotoSansPhoenician-Regular.ttf
+70e8f38000-70e8f40000 r--s 00000000 fc:00 245 /system/fonts/NotoSansLaoUI-Regular.ttf
+70e8f40000-70e8f45000 r-xp 00000000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f45000-70e8f5f000 ---p 00000000 00:00 0
+70e8f5f000-70e8f60000 r--p 0000f000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f60000-70e8f61000 rw-p 00010000 fc:00 2341 /system/lib64/libstagefright_codecbase.so
+70e8f61000-70e8f62000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70e8f62000-70e8f66000 r--s 00000000 fc:00 225 /system/fonts/NotoSansOldPersian-Regular.ttf
+70e8f66000-70e8f89000 r--s 00000000 fc:00 167 /system/fonts/NotoSansEthiopic-Regular.ttf
+70e8f89000-70e8f92000 r-xp 00000000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8f92000-70e8fa8000 ---p 00000000 00:00 0
+70e8fa8000-70e8fa9000 r--p 0000f000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8fa9000-70e8faa000 rw-p 00010000 fc:00 2515 /system/lib64/libGLESv1_CM.so
+70e8faa000-70e8fad000 r--s 00000000 fc:00 206 /system/fonts/NotoSansOsage-Regular.ttf
+70e8fad000-70e8fb5000 r--s 00000000 fc:00 121 /system/fonts/NotoSerifLao-Bold.ttf
+70e8fb5000-70e8fd3000 r--s 00000000 fc:00 68 /system/fonts/NotoNaskhArabicUI-Bold.ttf
+70e8fd3000-70e9010000 r-xp 00000000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e9010000-70e9026000 ---p 00000000 00:00 0
+70e9026000-70e902c000 r--p 0004a000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e902c000-70e902d000 rw-p 00050000 fc:00 2600 /system/lib64/android.hardware.cas@1.0.so
+70e902d000-70e902e000 r--s 00000000 00:05 31475 /dev/ashmem/5c7d41a6-003d-45a5-9e3b-2d34c5829a2d (deleted)
+70e902e000-70e9043000 r--s 00000000 fc:00 120 /system/fonts/NotoSansKannada-Regular.ttf
+70e9043000-70e9067000 r-xp 00000000 fc:00 2729 /system/lib64/libexpat.so
+70e9067000-70e9081000 ---p 00000000 00:00 0
+70e9081000-70e9083000 r--p 0002e000 fc:00 2729 /system/lib64/libexpat.so
+70e9083000-70e9084000 rw-p 00030000 fc:00 2729 /system/lib64/libexpat.so
+70e9084000-70e9086000 r--s 00000000 fc:00 155 /system/fonts/NotoSansOsmanya-Regular.ttf
+70e9086000-70e90c3000 r--s 00000000 fc:00 91 /system/fonts/NotoSerif-Regular.ttf
+70e90c3000-70e91ce000 r-xp 00000000 fc:00 2647 /system/lib64/libcrypto.so
+70e91ce000-70e91e2000 ---p 00000000 00:00 0
+70e91e2000-70e91f3000 r--p 0010f000 fc:00 2647 /system/lib64/libcrypto.so
+70e91f3000-70e91f4000 rw-p 00120000 fc:00 2647 /system/lib64/libcrypto.so
+70e91f4000-70e91f5000 rw-p 00000000 00:00 0 [anon:.bss]
+70e91f5000-70e91fa000 r--s 00000000 fc:00 149 /system/fonts/NotoSansNKo-Regular.ttf
+70e91fa000-70e9202000 r--s 00000000 fc:00 198 /system/fonts/NotoSerifLao-Regular.ttf
+70e9202000-70e921b000 r-xp 00000000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e921b000-70e922d000 ---p 00000000 00:00 0
+70e922d000-70e9230000 r--p 0001d000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e9230000-70e9231000 rw-p 00020000 fc:00 2682 /system/lib64/libmedia_helper.so
+70e9231000-70e924a000 r--s 00000000 fc:00 232 /system/fonts/NotoSansBengali-Bold.ttf
+70e924a000-70e9256000 r-xp 00000000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9256000-70e9272000 ---p 00000000 00:00 0
+70e9272000-70e9276000 r--p 0000c000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9276000-70e9277000 rw-p 00010000 fc:00 2467 /system/lib64/libsoundtrigger.so
+70e9277000-70e9278000 r--s 00000000 fc:01 1177 /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e9278000-70e927c000 r--s 00000000 fc:00 215 /system/fonts/NotoSansNewTaiLue-Regular.ttf
+70e927c000-70e929a000 r--s 00000000 fc:00 235 /system/fonts/NotoNaskhArabicUI-Regular.ttf
+70e929a000-70e929b000 r-xp 00000000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e929b000-70e92b9000 ---p 00000000 00:00 0
+70e92b9000-70e92ba000 r--p 0000f000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e92ba000-70e92bb000 rw-p 00010000 fc:00 2375 /system/lib64/android.hardware.graphics.common@1.1.so
+70e92bb000-70e92da000 r--s 00000000 fc:00 281 /system/fonts/GoogleSans-BoldItalic.ttf
+70e92da000-70e9302000 r-xp 00000000 fc:00 2529 /system/lib64/libinput.so
+70e9302000-70e931b000 ---p 00000000 00:00 0
+70e931b000-70e9322000 r--p 00029000 fc:00 2529 /system/lib64/libinput.so
+70e9322000-70e9323000 rw-p 00030000 fc:00 2529 /system/lib64/libinput.so
+70e9323000-70e9340000 r--s 00000000 fc:00 130 /system/fonts/NotoNaskhArabic-Bold.ttf
+70e9340000-70e934b000 r-xp 00000000 fc:00 2451 /system/lib64/libbpf.so
+70e934b000-70e935f000 ---p 00000000 00:00 0
+70e935f000-70e9360000 r--p 0000f000 fc:00 2451 /system/lib64/libbpf.so
+70e9360000-70e9361000 rw-p 00010000 fc:00 2451 /system/lib64/libbpf.so
+70e9361000-70e9367000 r--s 00000000 fc:00 96 /system/fonts/NotoSansKharoshthi-Regular.ttf
+70e9367000-70e9385000 r--s 00000000 fc:00 151 /system/fonts/GoogleSans-Bold.ttf
+70e9385000-70e93b8000 r-xp 00000000 fc:00 2494 /system/lib64/libpng.so
+70e93b8000-70e93d4000 ---p 00000000 00:00 0
+70e93d4000-70e93d5000 r--p 0003f000 fc:00 2494 /system/lib64/libpng.so
+70e93d5000-70e93d6000 rw-p 00040000 fc:00 2494 /system/lib64/libpng.so
+70e93d6000-70e93d7000 r--s 00004000 fc:01 1177 /vendor/overlay/Pixel/PixelThemeOverlay.apk
+70e93d7000-70e93d9000 r--s 00000000 fc:00 295 /system/fonts/NotoSansOldTurkic-Regular.ttf
+70e93d9000-70e93e5000 r--s 00000000 fc:00 86 /system/fonts/NotoSerifKhmer-Bold.otf
+70e93e5000-70e9404000 r--s 00000000 fc:00 240 /system/fonts/GoogleSans-MediumItalic.ttf
+70e9404000-70e9409000 r-xp 00000000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9409000-70e9423000 ---p 00000000 00:00 0
+70e9423000-70e9424000 r--p 0000f000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9424000-70e9425000 rw-p 00010000 fc:00 2404 /system/lib64/libhidlmemory.so
+70e9425000-70e943e000 r--s 00000000 fc:00 185 /system/fonts/NotoSansBengali-Regular.ttf
+70e943e000-70e945c000 r--s 00000000 fc:00 129 /system/fonts/GoogleSans-Medium.ttf
+70e945c000-70e960c000 r-xp 00000000 fc:00 2398 /system/lib64/libstagefright.so
+70e960c000-70e9625000 ---p 00000000 00:00 0
+70e9625000-70e9638000 r--p 001bd000 fc:00 2398 /system/lib64/libstagefright.so
+70e9638000-70e966c000 rw-p 001d0000 fc:00 2398 /system/lib64/libstagefright.so
+70e966c000-70e966d000 rw-p 00000000 00:00 0 [anon:.bss]
+70e966d000-70e966e000 r--s 00000000 103:1d 1474566 /data/resource-cache/vendor@overlay@Pixel@PixelThemeOverlay.apk@idmap
+70e966e000-70e9672000 r--s 00000000 fc:00 241 /system/fonts/NotoSansMeeteiMayek-Regular.ttf
+70e9672000-70e9680000 r--s 00000000 fc:00 150 /system/fonts/NotoSansMalayalamUI-Bold.ttf
+70e9680000-70e96a7000 r-xp 00000000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96a7000-70e96bd000 ---p 00000000 00:00 0
+70e96bd000-70e96bf000 r--p 0002e000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96bf000-70e96c0000 rw-p 00030000 fc:00 2365 /system/lib64/libhwbinder.so
+70e96c0000-70e96c1000 r--s 00088000 103:1d 1736830 /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk
+70e96c1000-70e96cb000 r--s 00000000 fc:00 94 /system/fonts/NotoSansKhmerUI-Regular.ttf
+70e96cb000-70e96d9000 r--s 00000000 fc:00 275 /system/fonts/NotoSansMalayalamUI-Regular.ttf
+70e96d9000-70e96dd000 r-xp 00000000 fc:00 2386 /system/lib64/libusbhost.so
+70e96dd000-70e96f8000 ---p 00000000 00:00 0
+70e96f8000-70e96f9000 r--p 0000f000 fc:00 2386 /system/lib64/libusbhost.so
+70e96f9000-70e96fa000 rw-p 00010000 fc:00 2386 /system/lib64/libusbhost.so
+70e96fa000-70e96fb000 r--p 00000000 00:05 10266154 /dev/ashmem/dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)
+70e96fb000-70e9701000 r--s 00000000 fc:00 280 /system/fonts/NotoSansCoptic-Regular.ttf
+70e9701000-70e9720000 r-xp 00000000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e9720000-70e973b000 ---p 00000000 00:00 0
+70e973b000-70e973e000 r--p 0002d000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e973e000-70e9740000 rw-p 00030000 fc:00 2490 /system/lib64/libstagefright_bufferqueue_helper.so
+70e9740000-70e9742000 r--s 00000000 fc:00 141 /system/fonts/NotoSansOldSouthArabian-Regular.ttf
+70e9742000-70e974c000 r--s 00000000 fc:00 229 /system/fonts/NotoSerifKhmer-Regular.otf
+70e974c000-70e9759000 r--s 00000000 fc:00 260 /system/fonts/NotoSerifMalayalam-Bold.ttf
+70e9759000-70e97c7000 r-xp 00000000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97c7000-70e97dc000 ---p 00000000 00:00 0
+70e97dc000-70e97e6000 r--p 00076000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97e6000-70e97ed000 rw-p 00080000 fc:00 2428 /system/lib64/libstagefright_omx.so
+70e97ed000-70e97fa000 r--s 00000000 fc:00 66 /system/fonts/NotoSerifMalayalam-Regular.ttf
+70e97fa000-70e9819000 r--s 00000000 fc:00 183 /system/fonts/GoogleSans-Italic.ttf
+70e9819000-70e9856000 r-xp 00000000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e9856000-70e9867000 ---p 00000000 00:00 0
+70e9867000-70e9869000 r--p 0003e000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e9869000-70e986a000 rw-p 00040000 fc:00 2434 /system/lib64/libprotobuf-cpp-lite.so
+70e986a000-70e9873000 r--s 00000000 fc:00 134 /system/fonts/NotoSansKhmerUI-Bold.ttf
+70e9873000-70e9891000 r--s 00000000 fc:00 81 /system/fonts/GoogleSans-Regular.ttf
+70e9891000-70e989e000 r-xp 00000000 fc:00 2377 /system/lib64/libmediametrics.so
+70e989e000-70e98ae000 ---p 00000000 00:00 0
+70e98ae000-70e98b0000 r--p 0000e000 fc:00 2377 /system/lib64/libmediametrics.so
+70e98b0000-70e98b1000 rw-p 00010000 fc:00 2377 /system/lib64/libmediametrics.so
+70e98b1000-70e98b9000 r--s 00000000 fc:00 152 /system/fonts/NotoSansLao-Bold.ttf
+70e98b9000-70e98c7000 r--s 00000000 fc:00 279 /system/fonts/NotoSansMalayalam-Bold.ttf
+70e98c7000-70e98ca000 r-xp 00000000 fc:00 2952 /system/lib64/libETC1.so
+70e98ca000-70e98e6000 ---p 00000000 00:00 0
+70e98e6000-70e98e7000 r--p 0000f000 fc:00 2952 /system/lib64/libETC1.so
+70e98e7000-70e98e8000 rw-p 00010000 fc:00 2952 /system/lib64/libETC1.so
+70e98e8000-70e98e9000 r--s 00000000 fc:00 1121 /system/usr/hyphen-data/hyph-und-ethi.hyb
+70e98e9000-70e98ef000 r--s 00000000 fc:00 64 /system/fonts/NotoSansBrahmi-Regular.ttf
+70e98ef000-70e990f000 rw-p 00000000 00:05 10271012 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e990f000-70e9926000 r-xp 00000000 fc:00 2526 /system/lib64/libbacktrace.so
+70e9926000-70e993e000 ---p 00000000 00:00 0
+70e993e000-70e993f000 r--p 0001f000 fc:00 2526 /system/lib64/libbacktrace.so
+70e993f000-70e9940000 rw-p 00020000 fc:00 2526 /system/lib64/libbacktrace.so
+70e9940000-70e9941000 r--s 00000000 fc:00 1129 /system/usr/hyphen-data/hyph-tk.hyb
+70e9941000-70e99a4000 r-xp 00000000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99a4000-70e99bc000 ---p 00000000 00:00 0
+70e99bc000-70e99c9000 r--p 00063000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99c9000-70e99d0000 rw-p 00070000 fc:00 2528 /system/lib64/libcamera_client.so
+70e99d0000-70e99d1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70e99d1000-70e99d3000 r--s 00000000 fc:00 200 /system/fonts/NotoSansOldItalic-Regular.ttf
+70e99d3000-70e99e1000 r--s 00000000 fc:00 153 /system/fonts/NotoSansMalayalam-Regular.ttf
+70e99e1000-70e9a01000 rw-p 00000000 00:05 10271011 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9a01000-70e9a1e000 r-xp 00000000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a1e000-70e9a39000 ---p 00000000 00:00 0
+70e9a39000-70e9a3c000 r--p 0001d000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a3c000-70e9a3f000 rw-p 00020000 fc:00 2542 /system/lib64/libimg_utils.so
+70e9a3f000-70e9a5c000 r--s 00000000 fc:00 262 /system/fonts/NotoNaskhArabic-Regular.ttf
+70e9a5c000-70e9a69000 r-xp 00000000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a69000-70e9a7b000 ---p 00000000 00:00 0
+70e9a7b000-70e9a7c000 r--p 0000f000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a7c000-70e9a7d000 rw-p 00010000 fc:00 2706 /system/lib64/libziparchive.so
+70e9a7d000-70e9a85000 r--s 00000000 fc:00 119 /system/fonts/NotoSansLao-Regular.ttf
+70e9a85000-70e9a8e000 r--s 00000000 fc:00 77 /system/fonts/NotoSansTamilUI-Bold.ttf
+70e9a8e000-70e9a97000 r--s 00000000 fc:00 160 /system/fonts/NotoSansTamilUI-Regular.ttf
+70e9a97000-70e9a9d000 r-xp 00000000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9a9d000-70e9ab6000 ---p 00000000 00:00 0
+70e9ab6000-70e9ab7000 r--p 0000f000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9ab7000-70e9ab8000 rw-p 00010000 fc:00 2536 /system/lib64/libnativehelper.so
+70e9ab8000-70e9ab9000 r--s 00000000 fc:00 1134 /system/usr/hyphen-data/hyph-te.hyb
+70e9ab9000-70e9ac2000 r--s 00000000 fc:00 246 /system/fonts/NotoSerifTamil-Bold.ttf
+70e9ac2000-70e9acb000 r--s 00000000 fc:00 302 /system/fonts/NotoSerifTamil-Regular.ttf
+70e9acb000-70e9af4000 r-xp 00000000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9af4000-70e9b09000 ---p 00000000 00:00 0
+70e9b09000-70e9b0b000 r--p 0002e000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9b0b000-70e9b0c000 rw-p 00030000 fc:00 2950 /system/lib64/libmemunreachable.so
+70e9b0c000-70e9b0d000 r--s 00000000 fc:00 1088 /system/usr/hyphen-data/hyph-ta.hyb
+70e9b0d000-70e9b0f000 r--s 00000000 fc:00 72 /system/fonts/NotoSansOlChiki-Regular.ttf
+70e9b0f000-70e9b2f000 rw-p 00000000 00:05 10271010 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9b2f000-70e9b4f000 r--s 00000000 00:10 16633 /dev/__properties__/u:object_r:persist_debug_prop:s0
+70e9b4f000-70e9b65000 r-xp 00000000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b65000-70e9b7b000 ---p 00000000 00:00 0
+70e9b7b000-70e9b7d000 r--p 0001e000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7d000-70e9b7e000 rw-p 00020000 fc:00 2920 /system/lib64/android.hardware.cas.native@1.0.so
+70e9b7e000-70e9b7f000 r--s 00000000 fc:00 1145 /system/usr/hyphen-data/hyph-pt.hyb
+70e9b7f000-70e9b83000 r--s 00000000 fc:00 277 /system/fonts/NotoSansMandaic-Regular.ttf
+70e9b83000-70e9bdb000 r-xp 00000000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bdb000-70e9bf2000 ---p 00000000 00:00 0
+70e9bf2000-70e9bf3000 r--p 0005f000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bf3000-70e9bf4000 rw-p 00060000 fc:00 2334 /system/lib64/libsonivox.so
+70e9bf4000-70e9bfb000 rw-p 00000000 00:00 0 [anon:.bss]
+70e9bfb000-70e9c01000 r--s 00000000 fc:00 123 /system/fonts/NotoSansCham-Bold.ttf
+70e9c01000-70e9c0a000 r--s 00000000 fc:00 255 /system/fonts/NotoSansTamil-Bold.ttf
+70e9c0a000-70e9c13000 r--s 00000000 fc:00 135 /system/fonts/NotoSansTamil-Regular.ttf
+70e9c13000-70e9c15000 r-xp 00000000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c15000-70e9c32000 ---p 00000000 00:00 0
+70e9c32000-70e9c33000 r--p 0000f000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c33000-70e9c34000 rw-p 00010000 fc:00 2519 /system/lib64/libgraphicsenv.so
+70e9c34000-70e9c3a000 r--s 00000000 fc:00 259 /system/fonts/NotoSansCham-Regular.ttf
+70e9c3a000-70e9c42000 r--s 00000000 fc:00 114 /system/fonts/NotoSansGurmukhiUI-Bold.ttf
+70e9c42000-70e9c4a000 r--s 00000000 fc:00 122 /system/fonts/NotoSansGurmukhiUI-Regular.ttf
+70e9c4a000-70e9c5f000 r-xp 00000000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c5f000-70e9c77000 ---p 00000000 00:00 0
+70e9c77000-70e9c79000 r--p 0001e000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c79000-70e9c7a000 rw-p 00020000 fc:00 2348 /system/lib64/android.hidl.allocator@1.0.so
+70e9c7a000-70e9c7b000 r--s 00000000 fc:00 1095 /system/usr/hyphen-data/hyph-pa.hyb
+70e9c7b000-70e9c83000 r--s 00000000 fc:00 298 /system/fonts/NotoSerifGurmukhi-Bold.otf
+70e9c83000-70e9d01000 r-xp 00000000 fc:00 2665 /system/lib64/libbinder.so
+70e9d01000-70e9d1e000 ---p 00000000 00:00 0
+70e9d1e000-70e9d2e000 r--p 00080000 fc:00 2665 /system/lib64/libbinder.so
+70e9d2e000-70e9d2f000 rw-p 00090000 fc:00 2665 /system/lib64/libbinder.so
+70e9d2f000-70e9d4f000 rw-p 00000000 00:05 10271009 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d4f000-70e9d53000 r-xp 00000000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d53000-70e9d6e000 ---p 00000000 00:00 0
+70e9d6e000-70e9d6f000 r--p 0000f000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d6f000-70e9d70000 rw-p 00010000 fc:00 2454 /system/lib64/libaudiomanager.so
+70e9d70000-70e9d71000 r--s 00000000 fc:00 1087 /system/usr/hyphen-data/hyph-or.hyb
+70e9d71000-70e9d91000 rw-p 00000000 00:05 10271008 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d91000-70e9e21000 r-xp 00000000 fc:00 2627 /system/lib64/libft2.so
+70e9e21000-70e9e37000 ---p 00000000 00:00 0
+70e9e37000-70e9e3c000 r--p 0009b000 fc:00 2627 /system/lib64/libft2.so
+70e9e3c000-70e9e3d000 rw-p 000a0000 fc:00 2627 /system/lib64/libft2.so
+70e9e3d000-70e9e3e000 r--s 00000000 fc:00 1142 /system/usr/hyphen-data/hyph-mr.hyb
+70e9e3e000-70e9e45000 r--s 00000000 fc:00 88 /system/fonts/NotoSerifGurmukhi-Regular.otf
+70e9e45000-70e9e65000 r--s 00000000 00:10 16594 /dev/__properties__/u:object_r:exported3_default_prop:s0
+70e9e65000-70e9e7f000 r-xp 00000000 fc:00 2643 /system/lib64/libunwind.so
+70e9e7f000-70e9e94000 ---p 00000000 00:00 0
+70e9e94000-70e9e95000 r--p 0001f000 fc:00 2643 /system/lib64/libunwind.so
+70e9e95000-70e9e96000 rw-p 00020000 fc:00 2643 /system/lib64/libunwind.so
+70e9e96000-70e9eff000 rw-p 00000000 00:00 0 [anon:.bss]
+70e9eff000-70e9f00000 r--s 00000000 fc:00 1130 /system/usr/hyphen-data/hyph-ml.hyb
+70e9f00000-70e9f02000 r--s 00000000 fc:00 75 /system/fonts/NotoSansOgham-Regular.ttf
+70e9f02000-70e9f0a000 r--s 00000000 fc:00 193 /system/fonts/NotoSansGurmukhi-Bold.ttf
+70e9f0a000-70ea022000 r-xp 00000000 fc:00 2328 /system/lib64/libsqlite.so
+70ea022000-70ea033000 ---p 00000000 00:00 0
+70ea033000-70ea036000 r--p 0011d000 fc:00 2328 /system/lib64/libsqlite.so
+70ea036000-70ea038000 rw-p 00120000 fc:00 2328 /system/lib64/libsqlite.so
+70ea038000-70ea03c000 r--s 00000000 fc:00 285 /system/fonts/NotoSansGlagolitic-Regular.ttf
+70ea03c000-70ea04c000 r--s 00000000 fc:00 184 /system/fonts/NotoSerifGujarati-Bold.ttf
+70ea04c000-70ea060000 r-xp 00000000 fc:00 2731 /system/lib64/libstatslog.so
+70ea060000-70ea07b000 ---p 00000000 00:00 0
+70ea07b000-70ea07c000 r--p 0001f000 fc:00 2731 /system/lib64/libstatslog.so
+70ea07c000-70ea07d000 rw-p 00020000 fc:00 2731 /system/lib64/libstatslog.so
+70ea07d000-70ea081000 r--s 00000000 fc:00 182 /system/fonts/NotoSansBatak-Regular.ttf
+70ea081000-70ea091000 r--s 00000000 fc:00 264 /system/fonts/NotoSerifGujarati-Regular.ttf
+70ea091000-70ea160000 r-xp 00000000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea160000-70ea173000 ---p 00000000 00:00 0
+70ea173000-70ea17a000 r--p 000d9000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea17a000-70ea17b000 rw-p 000e0000 fc:00 2728 /system/lib64/libdng_sdk.so
+70ea17b000-70ea198000 r--s 00000000 fc:00 223 /system/fonts/DancingScript-Bold.ttf
+70ea198000-70ea19c000 r-xp 00000000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea19c000-70ea1b7000 ---p 00000000 00:00 0
+70ea1b7000-70ea1b8000 r--p 0000f000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea1b8000-70ea1b9000 rw-p 00010000 fc:00 2344 /system/lib64/libnativewindow.so
+70ea1b9000-70ea1bd000 r--s 00000000 fc:00 98 /system/fonts/NotoSansAhom-Regular.otf
+70ea1bd000-70ea1d1000 r--s 00000000 fc:00 104 /system/fonts/NotoSerifDevanagari-Bold.ttf
+70ea1d1000-70ea1d4000 r-xp 00000000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1d4000-70ea1f0000 ---p 00000000 00:00 0
+70ea1f0000-70ea1f1000 r--p 0000f000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1f1000-70ea1f2000 rw-p 00010000 fc:00 2923 /system/lib64/libstdc++.so
+70ea1f2000-70ea1f4000 r--s 00000000 fc:00 117 /system/fonts/NotoSansLydian-Regular.ttf
+70ea1f4000-70ea211000 r--s 00000000 fc:00 239 /system/fonts/DancingScript-Regular.ttf
+70ea211000-70ea24a000 r-xp 00000000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea24a000-70ea268000 ---p 00000000 00:00 0
+70ea268000-70ea26c000 r--p 0003c000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26c000-70ea26d000 rw-p 00040000 fc:00 2933 /system/lib64/android.hardware.graphics.bufferqueue@1.0.so
+70ea26d000-70ea26f000 r--s 00000000 fc:00 244 /system/fonts/NotoSansLycian-Regular.ttf
+70ea26f000-70ea273000 r--s 00000000 fc:00 173 /system/fonts/NotoSansThaana-Bold.ttf
+70ea273000-70ea287000 r--s 00000000 fc:00 106 /system/fonts/NotoSerifDevanagari-Regular.ttf
+70ea287000-70ea29e000 r-xp 00000000 fc:00 2938 /system/lib64/libpiex.so
+70ea29e000-70ea2b6000 ---p 00000000 00:00 0
+70ea2b6000-70ea2b7000 r--p 0001f000 fc:00 2938 /system/lib64/libpiex.so
+70ea2b7000-70ea2b8000 rw-p 00020000 fc:00 2938 /system/lib64/libpiex.so
+70ea2b8000-70ea2c0000 r--s 00000000 fc:00 113 /system/fonts/NotoSansGurmukhi-Regular.ttf
+70ea2c0000-70ea2c6000 r--s 00000000 fc:00 263 /system/fonts/NotoSerifGeorgian-Bold.ttf
+70ea2c6000-70ea2cc000 r--s 00000000 fc:00 249 /system/fonts/NotoSerifGeorgian-Regular.ttf
+70ea2cc000-70ea9b4000 r-xp 00000000 fc:00 2532 /system/lib64/libhwui.so
+70ea9b4000-70ea9d3000 ---p 00000000 00:00 0
+70ea9d3000-70eaa0b000 r--p 006e8000 fc:00 2532 /system/lib64/libhwui.so
+70eaa0b000-70eaa0c000 rw-p 00720000 fc:00 2532 /system/lib64/libhwui.so
+70eaa0c000-70eaa11000 rw-p 00000000 00:00 0 [anon:.bss]
+70eaa11000-70eaa12000 r--s 00000000 fc:00 1110 /system/usr/hyphen-data/hyph-la.hyb
+70eaa12000-70eaa16000 r--s 00000000 fc:00 87 /system/fonts/NotoSansThaana-Regular.ttf
+70eaa16000-70eaa1b000 r--s 00000000 fc:00 218 /system/fonts/NotoSansGeorgian-Bold.ttf
+70eaa1b000-70eaa20000 r--s 00000000 fc:00 125 /system/fonts/NotoSansGeorgian-Regular.ttf
+70eaa20000-70eaa40000 rw-p 00000000 00:05 10271007 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaa40000-70eaaa0000 r-xp 00000000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaaa0000-70eaabe000 ---p 00000000 00:00 0
+70eaabe000-70eaac6000 r--p 00068000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaac6000-70eaac7000 rw-p 00070000 fc:00 2384 /system/lib64/libhidltransport.so
+70eaac7000-70eaacb000 r--s 00000000 fc:00 192 /system/fonts/NotoSerifArmenian-Bold.ttf
+70eaacb000-70eaad0000 r--s 00000000 fc:00 210 /system/fonts/NotoSansThaiUI-Bold.ttf
+70eaad0000-70eaaf0000 rw-p 00000000 00:05 10271006 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eab10000-70eab57000 r-xp 00000000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab57000-70eab6d000 ---p 00000000 00:00 0
+70eab6d000-70eab7a000 r--p 00053000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab7a000-70eab7f000 rw-p 00060000 fc:00 2546 /system/lib64/libmedia_omx.so
+70eab7f000-70eab80000 r--s 00000000 fc:00 1119 /system/usr/hyphen-data/hyph-kn.hyb
+70eab80000-70eab86000 r--s 00000000 fc:00 224 /system/fonts/NotoSansThaiUI-Regular.ttf
+70eab86000-70eab8b000 r--s 00000000 fc:00 300 /system/fonts/NotoSerifThai-Bold.ttf
+70eab8b000-70eabab000 rw-p 00000000 00:05 10271004 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eabab000-70eac21000 r-xp 00000000 fc:00 2385 /system/lib64/libvintf.so
+70eac21000-70eac31000 ---p 00000000 00:00 0
+70eac31000-70eac36000 r--p 0007b000 fc:00 2385 /system/lib64/libvintf.so
+70eac36000-70eac37000 rw-p 00080000 fc:00 2385 /system/lib64/libvintf.so
+70eac37000-70eac39000 rw-p 00000000 00:00 0 [anon:.bss]
+70eac39000-70eac3a000 r--s 00000000 fc:00 1104 /system/usr/hyphen-data/hyph-hy.hyb
+70eac3a000-70eac3f000 r--s 00000000 fc:00 212 /system/fonts/NotoSerifThai-Regular.ttf
+70eac3f000-70eac44000 r--s 00000000 fc:00 220 /system/fonts/NotoSansThai-Bold.ttf
+70eac44000-70eacb2000 r-xp 00000000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacb2000-70eaccf000 ---p 00000000 00:00 0
+70eaccf000-70eacd8000 r--p 00077000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacd8000-70eacd9000 rw-p 00080000 fc:00 2606 /system/lib64/android.hardware.media.omx@1.0.so
+70eacd9000-70eacdf000 r--s 00000000 fc:00 169 /system/fonts/NotoSansThai-Regular.ttf
+70eacdf000-70eace9000 r--s 00000000 fc:00 140 /system/fonts/CarroisGothicSC-Regular.ttf
+70eace9000-70ead09000 rw-p 00000000 00:05 10271003 /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70ead09000-70ead22000 r-xp 00000000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead22000-70ead34000 ---p 00000000 00:00 0
+70ead34000-70ead37000 r--p 0001d000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead37000-70ead38000 rw-p 00020000 fc:00 2539 /system/lib64/android.hardware.graphics.mapper@2.1.so
+70ead38000-70ead47000 r--s 00000000 fc:00 188 /system/fonts/ComingSoon.ttf
+70ead47000-70ead5d000 r-xp 00000000 fc:00 2379 /system/lib64/libselinux.so
+70ead5d000-70ead76000 ---p 00000000 00:00 0
+70ead76000-70ead77000 r--p 0001f000 fc:00 2379 /system/lib64/libselinux.so
+70ead77000-70ead78000 rw-p 00020000 fc:00 2379 /system/lib64/libselinux.so
+70ead78000-70ead79000 rw-p 00000000 00:00 0 [anon:.bss]
+70ead79000-70ead7d000 r--s 00000000 fc:00 282 /system/fonts/NotoSerifArmenian-Regular.ttf
+70ead7d000-70ead82000 r--s 00000000 fc:00 288 /system/fonts/NotoSerifHebrew-Bold.ttf
+70ead82000-70ead83000 r-xp 00000000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70ead83000-70eada1000 ---p 00000000 00:00 0
+70eada1000-70eada2000 r--p 0000f000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70eada2000-70eada3000 rw-p 00010000 fc:00 2680 /system/lib64/android.hardware.media@1.0.so
+70eada3000-70eada8000 r--s 00000000 fc:00 248 /system/fonts/NotoSerifHebrew-Regular.ttf
+70eada8000-70eadb9000 r--s 00000000 fc:00 252 /system/fonts/CutiveMono.ttf
+70eadb9000-70eadd9000 r--s 00000000 00:10 16641 /dev/__properties__/u:object_r:radio_prop:s0
+70eadd9000-70eadda000 r-xp 00000000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadda000-70eadf8000 ---p 00000000 00:00 0
+70eadf8000-70eadf9000 r--p 0000f000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadf9000-70eadfa000 rw-p 00010000 fc:00 2533 /system/lib64/android.hardware.graphics.common@1.0.so
+70eadfa000-70eadfb000 r--s 00000000 fc:00 1126 /system/usr/hyphen-data/hyph-hr.hyb
+70eadfb000-70eadfd000 r--s 00000000 fc:00 194 /system/fonts/NotoSansLisu-Regular.ttf
+70eadfd000-70eae18000 r--s 00000000 fc:00 201 /system/fonts/DroidSansMono.ttf
+70eae18000-70eae3b000 r-xp 00000000 fc:00 2925 /system/lib64/liblzma.so
+70eae3b000-70eae57000 ---p 00000000 00:00 0
+70eae57000-70eae58000 r--p 0002f000 fc:00 2925 /system/lib64/liblzma.so
+70eae58000-70eae59000 rw-p 00030000 fc:00 2925 /system/lib64/liblzma.so
+70eae59000-70eae5f000 rw-p 00000000 00:00 0 [anon:.bss]
+70eae5f000-70eae62000 r--s 00000000 fc:00 103 /system/fonts/NotoSansLimbu-Regular.ttf
+70eae62000-70eae67000 r--s 00000000 fc:00 236 /system/fonts/NotoSansHebrew-Bold.ttf
+70eae67000-70eae84000 r--s 001c2000 fc:00 990 /system/framework/ext.jar
+70eae84000-70eaea4000 rw-p 00000000 00:05 10269720 /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eaea4000-70eaede000 r-xp 00000000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaede000-70eaefa000 ---p 00000000 00:00 0
+70eaefa000-70eaeff000 r--p 0003b000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaeff000-70eaf00000 rw-p 00040000 fc:00 2924 /system/lib64/libwilhelm.so
+70eaf00000-70eaf03000 r--s 00000000 fc:00 242 /system/fonts/NotoSansElbasan-Regular.otf
+70eaf03000-70eaf21000 r-xp 00000000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf21000-70eaf38000 ---p 00000000 00:00 0
+70eaf38000-70eaf3d000 r--p 0002b000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf3d000-70eaf3e000 rw-p 00030000 fc:00 2415 /system/lib64/libdrmframework.so
+70eaf3e000-70eaf43000 r--s 00000000 fc:00 70 /system/fonts/NotoSansHebrew-Regular.ttf
+70eaf43000-70eaf44000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eaf44000-70eaf48000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eaf48000-70eaf49000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eaf49000-70eaf4c000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eaf4c000-70eaf4d000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eaf4d000-70eaf4e000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eaf4e000-70eaf52000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eaf52000-70eaf98000 r-xp 00000000 fc:00 2426 /system/lib64/libunwindstack.so
+70eaf98000-70eafb6000 ---p 00000000 00:00 0
+70eafb6000-70eafbd000 r--p 00049000 fc:00 2426 /system/lib64/libunwindstack.so
+70eafbd000-70eafbe000 rw-p 00050000 fc:00 2426 /system/lib64/libunwindstack.so
+70eafbe000-70eafc0000 r--s 00000000 fc:00 162 /system/fonts/NotoSansKayahLi-Regular.ttf
+70eafc0000-70eafe4000 r-xp 00000000 fc:00 2944 /system/lib64/libvulkan.so
+70eafe4000-70eaffc000 ---p 00000000 00:00 0
+70eaffc000-70eaffe000 r--p 0002e000 fc:00 2944 /system/lib64/libvulkan.so
+70eaffe000-70eafff000 rw-p 00030000 fc:00 2944 /system/lib64/libvulkan.so
+70eafff000-70eb001000 r--s 00000000 fc:00 180 /system/fonts/NotoSansInscriptionalParthian-Regular.ttf
+70eb001000-70eb01d000 r-xp 00000000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb01d000-70eb030000 ---p 00000000 00:00 0
+70eb030000-70eb031000 r--p 0001f000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb031000-70eb032000 rw-p 00020000 fc:00 2400 /system/lib64/libbufferhubqueue.so
+70eb032000-70eb036000 r--s 00000000 fc:00 269 /system/fonts/NotoSansArmenian-Bold.ttf
+70eb036000-70eb037000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb037000-70eb03a000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb03a000-70eb03b000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb03b000-70eb03c000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb03c000-70eb040000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb040000-70eb042000 r-xp 00000000 fc:00 2935 /system/lib64/libhardware.so
+70eb042000-70eb05f000 ---p 00000000 00:00 0
+70eb05f000-70eb060000 r--p 0000f000 fc:00 2935 /system/lib64/libhardware.so
+70eb060000-70eb061000 rw-p 00010000 fc:00 2935 /system/lib64/libhardware.so
+70eb061000-70eb063000 r--s 00000000 fc:00 171 /system/fonts/NotoSansInscriptionalPahlavi-Regular.ttf
+70eb063000-70eb064000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb064000-70eb067000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb067000-70eb068000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb068000-70eb069000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb069000-70eb06d000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb06d000-70eb06e000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb06e000-70eb071000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb071000-70eb072000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb072000-70eb073000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb073000-70eb077000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb077000-70eb078000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb078000-70eb07b000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb07b000-70eb07c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb07c000-70eb07d000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb07d000-70eb081000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb081000-70eb082000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb082000-70eb085000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb085000-70eb086000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb086000-70eb09d000 r-xp 00000000 fc:00 2604 /system/lib64/libz.so
+70eb09d000-70eb0b5000 ---p 00000000 00:00 0
+70eb0b5000-70eb0b6000 r--p 0001f000 fc:00 2604 /system/lib64/libz.so
+70eb0b6000-70eb0b7000 rw-p 00020000 fc:00 2604 /system/lib64/libz.so
+70eb0b7000-70eb0bb000 r--s 00000000 fc:00 289 /system/fonts/NotoSansArmenian-Regular.ttf
+70eb0bb000-70eb0bc000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb0bc000-70eb0c0000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb0c0000-70eb0c1000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0c1000-70eb0c4000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb0c4000-70eb0c5000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0c5000-70eb0c6000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70eb0c6000-70eb0ca000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70eb0ca000-70eb0cb000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0cb000-70eb0ce000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70eb0ce000-70eb0cf000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70eb0cf000-70eb0ef000 rw-p 00000000 00:05 10270988 /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eb0ef000-70eb5bb000 r-xp 00000000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5bb000-70eb5cf000 ---p 00000000 00:00 0
+70eb5cf000-70eb5e6000 r--p 004d9000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5e6000-70eb5ea000 rw-p 004f0000 fc:00 2374 /system/lib64/libpdfium.so
+70eb5ea000-70eb5f1000 rw-p 00000000 00:00 0 [anon:.bss]
+70eb5f1000-70eb5f2000 r--s 00000000 fc:00 1094 /system/usr/hyphen-data/hyph-hi.hyb
+70eb5f2000-70eb5f6000 rw-p 00000000 00:05 10270982 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb602000-70eb606000 rw-p 00000000 00:05 10270978 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb606000-70eb60a000 rw-p 00000000 00:05 10270977 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb60e000-70eb612000 rw-p 00000000 00:05 10270975 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb612000-70eb616000 rw-p 00000000 00:05 10270974 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb616000-70eb61a000 r-xp 00000000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb61a000-70eb635000 ---p 00000000 00:00 0
+70eb635000-70eb636000 r--p 0000f000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb636000-70eb637000 rw-p 00010000 fc:00 2479 /system/lib64/libspeexresampler.so
+70eb637000-70eb639000 r--s 00000000 fc:00 299 /system/fonts/NotoSansImperialAramaic-Regular.ttf
+70eb639000-70eb63d000 rw-p 00000000 00:05 10270973 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb63d000-70eb641000 rw-p 00000000 00:05 10270972 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb641000-70eb645000 rw-p 00000000 00:05 10270971 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb645000-70eb649000 rw-p 00000000 00:05 10270970 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb649000-70eb64d000 rw-p 00000000 00:05 10270969 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb64d000-70eb651000 rw-p 00000000 00:05 10270968 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb651000-70eb655000 rw-p 00000000 00:05 10270967 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb655000-70eb659000 rw-p 00000000 00:05 10270966 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb659000-70eb65d000 rw-p 00000000 00:05 10270965 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb65d000-70eb661000 rw-p 00000000 00:05 10270964 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb661000-70eb6c5000 r-xp 00000000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6c5000-70eb6df000 ---p 00000000 00:00 0
+70eb6df000-70eb6e1000 r--p 0006e000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6e1000-70eb6e2000 rw-p 00070000 fc:00 2461 /system/lib64/libhidl-gen-utils.so
+70eb6e2000-70eb6e6000 rw-p 00000000 00:05 10270963 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb702000-70eb706000 rw-p 00000000 00:05 10270955 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb706000-70eb70a000 rw-p 00000000 00:05 10270954 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb70e000-70eb712000 rw-p 00000000 00:05 10270952 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb712000-70eb71a000 r-xp 00000000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb71a000-70eb72f000 ---p 00000000 00:00 0
+70eb72f000-70eb730000 r--p 0000f000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb730000-70eb732000 rw-p 00010000 fc:00 2652 /system/lib64/libcamera_metadata.so
+70eb732000-70eb734000 r--s 00000000 fc:00 131 /system/fonts/NotoSansHanunoo-Regular.ttf
+70eb734000-70eb738000 rw-p 00000000 00:05 10270951 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb738000-70eb73c000 rw-p 00000000 00:05 10270950 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb73c000-70eb740000 rw-p 00000000 00:05 10270949 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb740000-70eb744000 rw-p 00000000 00:05 10270948 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb744000-70eb748000 rw-p 00000000 00:05 10270947 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb748000-70eb74c000 rw-p 00000000 00:05 10270946 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb74c000-70eb750000 rw-p 00000000 00:05 10270945 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb750000-70eb754000 rw-p 00000000 00:05 10270944 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb754000-70eb758000 rw-p 00000000 00:05 10270943 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb758000-70eb75c000 rw-p 00000000 00:05 10270942 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb75c000-70eb760000 rw-p 00000000 00:05 10270941 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb760000-70eb764000 rw-p 00000000 00:05 10270940 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb764000-70eb767000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb767000-70eb768000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb768000-70eb76c000 rw-p 00000000 00:05 10270939 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb76c000-70eb770000 rw-p 00000000 00:05 10270938 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb770000-70eb771000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb771000-70eb774000 r--s 00000000 fc:00 231 /system/fonts/NotoSansDeseret-Regular.ttf
+70eb774000-70eb778000 rw-p 00000000 00:05 10270937 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb778000-70eb77c000 rw-p 00000000 00:05 10270936 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb77c000-70eb780000 rw-p 00000000 00:05 10270935 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb780000-70eb784000 rw-p 00000000 00:05 10270934 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb784000-70eb788000 rw-p 00000000 00:05 10270933 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb788000-70eb78c000 rw-p 00000000 00:05 10270932 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb78c000-70eb790000 rw-p 00000000 00:05 10270931 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb790000-70eb794000 rw-p 00000000 00:05 10270930 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb794000-70eb798000 rw-p 00000000 00:05 10270929 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb798000-70eb79c000 rw-p 00000000 00:05 10270928 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a0000-70eb7a1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7a1000-70eb7a3000 r--s 00000000 fc:00 176 /system/fonts/NotoSansGothic-Regular.ttf
+70eb7a3000-70eb7a7000 rw-p 00000000 00:05 10270926 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a7000-70eb7a8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7a8000-70eb7a9000 r--s 00000000 fc:00 1109 /system/usr/hyphen-data/hyph-gu.hyb
+70eb7a9000-70eb7ad000 rw-p 00000000 00:05 10270925 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ad000-70eb7ae000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7ae000-70eb7af000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb7af000-70eb7b0000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7b0000-70eb7b2000 r--s 00000000 fc:00 191 /system/fonts/NotoSansCypriot-Regular.ttf
+70eb7b2000-70eb7b6000 rw-p 00000000 00:05 10270924 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7d6000-70eb7d7000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7e7000-70eb7e8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb7e8000-70eb7ea000 r--s 00000000 fc:00 174 /system/fonts/NotoSansCarian-Regular.ttf
+70eb7ea000-70eb7ee000 rw-p 00000000 00:05 10270911 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7f6000-70eb7f7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb7f7000-70eb7f8000 r--s 00000000 fc:00 1096 /system/usr/hyphen-data/hyph-eu.hyb
+70eb7f8000-70eb7fc000 rw-p 00000000 00:05 10270908 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb800000-70eb804000 rw-p 00000000 00:05 10270906 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb804000-70eb808000 rw-p 00000000 00:05 10270905 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb808000-70eb80c000 rw-p 00000000 00:05 10270904 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb80c000-70eb810000 rw-p 00000000 00:05 10270903 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb810000-70eb814000 rw-p 00000000 00:05 10270902 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb814000-70eb815000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70eb815000-70eb819000 rw-p 00000000 00:05 10270901 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb819000-70eb81d000 rw-p 00000000 00:05 10270900 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb81d000-70eb81e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb81e000-70eb822000 rw-p 00000000 00:05 10270899 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb822000-70eb826000 rw-p 00000000 00:05 10270898 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb826000-70eb82a000 rw-p 00000000 00:05 10270897 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb82e000-70eb832000 rw-p 00000000 00:05 10270895 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb832000-70eb836000 rw-p 00000000 00:05 10270894 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb836000-70eb837000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb837000-70eb83b000 rw-p 00000000 00:05 10270893 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb83f000-70eb843000 rw-p 00000000 00:05 10270891 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb843000-70eb847000 rw-p 00000000 00:05 10270890 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb847000-70eb84b000 rw-p 00000000 00:05 10270889 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb84f000-70eb850000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb850000-70eb854000 rw-p 00000000 00:05 10270887 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb854000-70eb858000 rw-p 00000000 00:05 10270886 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb858000-70eb85c000 rw-p 00000000 00:05 10270885 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb85c000-70eb860000 rw-p 00000000 00:05 10270884 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb860000-70eb864000 rw-p 00000000 00:05 10270883 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb864000-70eb868000 rw-p 00000000 00:05 10270882 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb868000-70eb869000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb869000-70eb86d000 rw-p 00000000 00:05 10270881 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb86d000-70eb871000 rw-p 00000000 00:05 10270880 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb871000-70eb875000 rw-p 00000000 00:05 10270879 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb875000-70eb879000 rw-p 00000000 00:05 10270878 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb879000-70eb87d000 rw-p 00000000 00:05 10270877 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb87d000-70eb881000 rw-p 00000000 00:05 10270876 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb881000-70eb885000 rw-p 00000000 00:05 10270875 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb885000-70eb889000 rw-p 00000000 00:05 10270874 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb889000-70eb88d000 rw-p 00000000 00:05 10270873 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb88d000-70eb891000 rw-p 00000000 00:05 10270872 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb891000-70eb892000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb892000-70eb896000 rw-p 00000000 00:05 10270871 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb896000-70eb89a000 rw-p 00000000 00:05 10270870 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb89a000-70eb89b000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70eb89b000-70eb89c000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb89c000-70eb8a0000 rw-p 00000000 00:05 10270869 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a4000-70eb8a5000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8bd000-70eb8be000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb8be000-70eb8c1000 r--s 00000000 fc:00 168 /system/fonts/NotoSansAvestan-Regular.ttf
+70eb8c1000-70eb8c5000 rw-p 00000000 00:05 10270861 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8d5000-70eb8d7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8eb000-70eb8ec000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb8ec000-70eb8ed000 r--s 00000000 fc:00 1099 /system/usr/hyphen-data/hyph-bn.hyb
+70eb8ed000-70eb8f1000 rw-p 00000000 00:05 10270851 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb901000-70eb905000 rw-p 00000000 00:05 10270846 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb905000-70eb909000 rw-p 00000000 00:05 10270845 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb909000-70eb90d000 rw-p 00000000 00:05 10270844 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb90d000-70eb911000 rw-p 00000000 00:05 10270843 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb911000-70eb915000 rw-p 00000000 00:05 10270842 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb915000-70eb916000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb916000-70eb917000 r--s 00000000 fc:00 1114 /system/usr/hyphen-data/hyph-bg.hyb
+70eb917000-70eb91b000 rw-p 00000000 00:05 10270841 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb91b000-70eb91c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb91c000-70eb91d000 r--s 00000000 fc:00 1133 /system/usr/hyphen-data/hyph-as.hyb
+70eb91d000-70eb921000 rw-p 00000000 00:05 10270840 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb921000-70eb925000 rw-p 00000000 00:05 10270839 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb925000-70eb926000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70eb926000-70eb927000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb927000-70eb929000 r--s 00000000 fc:00 203 /system/fonts/NotoSansBuhid-Regular.ttf
+70eb929000-70eb92d000 rw-p 00000000 00:05 10270838 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb92d000-70eb931000 rw-p 00000000 00:05 10270837 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb931000-70eb935000 rw-p 00000000 00:05 10270836 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb935000-70eb939000 rw-p 00000000 00:05 10270835 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb939000-70eb93d000 rw-p 00000000 00:05 10270834 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb93d000-70eb941000 rw-p 00000000 00:05 10270833 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb941000-70eb945000 rw-p 00000000 00:05 10270832 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb945000-70eb949000 rw-p 00000000 00:05 10270831 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb949000-70eb94d000 rw-p 00000000 00:05 10270830 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb94d000-70eb951000 rw-p 00000000 00:05 10270829 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb951000-70eb991000 rw-p 00000000 00:05 10270722 /dev/ashmem/dalvik-mark stack (deleted)
+70eb991000-70eb992000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb992000-70eb996000 rw-p 00000000 00:05 10270828 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb996000-70eb99a000 rw-p 00000000 00:05 10270827 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9a2000-70eb9a4000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9b0000-70eb9b1000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9b1000-70eb9b2000 r--s 00021000 fc:01 1180 /vendor/overlay/framework-res__auto_generated_rro.apk
+70eb9b2000-70eb9b6000 rw-p 00000000 00:05 10270821 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ce000-70eb9cf000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb9cf000-70eb9d1000 r--s 00000000 fc:00 213 /system/fonts/NotoSansBuginese-Regular.ttf
+70eb9d1000-70eb9d5000 rw-p 00000000 00:05 10270814 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9f5000-70eb9f6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002 /dev/ashmem/dalvik-indirect ref table (deleted)
+70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9fc000-70eb9fd000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999 /dev/ashmem/dalvik-indirect ref table (deleted)
+70eb9ff000-70eba00000 r--s 00000000 fc:00 983 /system/framework/com.google.vr.platform.jar
+70eba00000-70eba04000 rw-p 00000000 00:05 10270804 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba04000-70eba08000 rw-p 00000000 00:05 10270803 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba08000-70eba0c000 rw-p 00000000 00:05 10270802 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba0c000-70eba10000 rw-p 00000000 00:05 10270801 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba10000-70eba14000 rw-p 00000000 00:05 10270800 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba14000-70eba18000 rw-p 00000000 00:05 10270799 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba18000-70eba1c000 rw-p 00000000 00:05 10270798 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba1c000-70eba20000 rw-p 00000000 00:05 10270797 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba20000-70eba24000 rw-p 00000000 00:05 10270796 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba24000-70eba28000 rw-p 00000000 00:05 10270795 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba28000-70eba2c000 rw-p 00000000 00:05 10270794 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba2c000-70eba2d000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eba2d000-70eba2e000 r--s 00000000 fc:00 881 /system/framework/android.test.base.jar
+70eba2e000-70eba2f000 r--s 00000000 fc:00 707 /system/framework/framework-oahl-backward-compatibility.jar
+70eba2f000-70eba30000 r--s 00000000 fc:00 705 /system/framework/android.hidl.manager-V1.0-java.jar
+70eba30000-70eba34000 rw-p 00000000 00:05 10270793 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba34000-70eba38000 rw-p 00000000 00:05 10270792 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba38000-70eba3c000 rw-p 00000000 00:05 10270791 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba3c000-70eba40000 rw-p 00000000 00:05 10270790 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba40000-70eba44000 rw-p 00000000 00:05 10270789 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba44000-70eba48000 rw-p 00000000 00:05 10270788 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba48000-70eba4c000 rw-p 00000000 00:05 10270787 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba4c000-70eba50000 rw-p 00000000 00:05 10270786 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba50000-70eba52000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70eba52000-70eba53000 r--s 00000000 fc:00 971 /system/framework/android.hidl.base-V1.0-java.jar
+70eba53000-70eba57000 rw-p 00000000 00:05 10270785 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba57000-70eba5b000 rw-p 00000000 00:05 10270784 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba5f000-70eba63000 rw-p 00000000 00:05 10270782 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba63000-70eba64000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70eba64000-70eba65000 r--s 00000000 fc:00 889 /system/framework/ims-common.jar
+70eba65000-70eba69000 rw-p 00000000 00:05 10270781 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba69000-70eba6d000 rw-p 00000000 00:05 10270780 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba6d000-70eba71000 rw-p 00000000 00:05 10270779 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba71000-70eba75000 rw-p 00000000 00:05 10270778 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba75000-70eba95000 rw-p 00000000 00:05 10267647 /dev/ashmem/dalvik-large marked objects (deleted)
+70eba95000-70ebab5000 rw-p 00000000 00:05 10267646 /dev/ashmem/dalvik-large live objects (deleted)
+70ebab5000-70ebab6000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebab6000-70ebab7000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebab7000-70ebabb000 rw-p 00000000 00:05 10270777 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebabb000-70ebadb000 r--s 00000000 00:10 16603 /dev/__properties__/u:object_r:exported_fingerprint_prop:s0
+70ebadb000-70ebadc000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebadc000-70ebadd000 r--s 00000000 fc:00 878 /system/framework/voip-common.jar
+70ebadd000-70ebadf000 rw-p 00000000 00:05 10270995 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebaef000-70ebb0f000 r--s 00000000 00:10 16582 /dev/__properties__/u:object_r:debug_prop:s0
+70ebb0f000-70ebb10000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebb10000-70ebb11000 r--s 00000000 fc:00 703 /system/framework/telephony-common.jar
+70ebb11000-70ebb13000 rw-p 00000000 00:05 10270994 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb17000-70ebb19000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb1d000-70ebb3d000 r--s 00000000 00:10 16600 /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebb3d000-70ebb5d000 r--s 00000000 00:10 16650 /dev/__properties__/u:object_r:system_prop:s0
+70ebb5d000-70ebb7d000 r--s 00000000 00:10 16610 /dev/__properties__/u:object_r:exported_vold_prop:s0
+70ebb7d000-70ebb9d000 r--s 00000000 00:10 16598 /dev/__properties__/u:object_r:exported_config_prop:s0
+70ebb9d000-70ebb9e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebb9e000-70ebba2000 rw-p 00000000 00:05 10270770 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebba6000-70ebba7000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebba7000-70ebba8000 r--s 00000000 fc:00 1004 /system/framework/framework.jar
+70ebba8000-70ebbac000 rw-p 00000000 00:05 10270768 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbc4000-70ebbe4000 r--s 00000000 00:10 16581 /dev/__properties__/u:object_r:dalvik_prop:s0
+70ebbe4000-70ebbe5000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebbe5000-70ebbe6000 r--s 00004000 fc:00 877 /system/framework/apache-xml.jar
+70ebbe6000-70ebbe8000 rw-p 00000000 00:05 10270993 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbf8000-70ebbf9000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebbf9000-70ebbfa000 r--s 00000000 fc:00 968 /system/framework/bouncycastle.jar
+70ebbfa000-70ebbfc000 rw-p 00000000 00:05 10270992 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc14000-70ebc15000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc15000-70ebc16000 r--s 00000000 fc:00 960 /system/framework/okhttp.jar
+70ebc16000-70ebc1a000 rw-p 00000000 00:05 10270751 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc1e000-70ebc3e000 r--s 00000000 00:10 16584 /dev/__properties__/u:object_r:default_prop:s0
+70ebc3e000-70ebc3f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc3f000-70ebc40000 r--s 00000000 fc:00 974 /system/framework/conscrypt.jar
+70ebc40000-70ebc42000 rw-p 00000000 00:05 10269719 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc4e000-70ebc4f000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc5d000-70ebc7d000 r--s 00000000 00:10 16599 /dev/__properties__/u:object_r:exported_dalvik_prop:s0
+70ebc7d000-70ebc7e000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc7e000-70ebc7f000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc7f000-70ebc80000 r--s 00004000 fc:00 963 /system/framework/core-libart.jar
+70ebc80000-70ebc81000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc81000-70ebc85000 rw-p 00000000 00:05 10270743 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc89000-70ebc8a000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebc8a000-70ebc8c000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc8c000-70ebc8d000 r--s 0001e000 fc:00 699 /system/framework/core-oj.jar
+70ebc8d000-70ebc91000 rw-p 00000000 00:05 10270741 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc99000-70ebc9b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebc9b000-70ebc9c000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebc9c000-70ebca0000 rw-p 00000000 00:05 10270738 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcb0000-70ebcd0000 r--s 00000000 00:10 16592 /dev/__properties__/u:object_r:exported2_system_prop:s0
+70ebcd0000-70ebcd1000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebcd1000-70ebcd5000 rw-p 00000000 00:05 10270733 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebce9000-70ebcea000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987 /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebcec000-70ebcf9000 r--p 00646000 103:1d 639532 /data/dalvik-cache/arm64/system@framework@boot-framework.art
+70ebcf9000-70ebd19000 r--s 00000000 00:10 16620 /dev/__properties__/u:object_r:log_tag_prop:s0
+70ebd19000-70ebd39000 r--s 00000000 00:10 16621 /dev/__properties__/u:object_r:logd_prop:s0
+70ebd39000-70ebd3a000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebd3a000-70ebd3b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd3b000-70ebd3f000 rw-p 00000000 00:05 10270727 /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebd3f000-70ebd40000 r--p 00002000 103:1d 639556 /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
+70ebd40000-70ebd41000 r--p 00005000 103:1d 639553 /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
+70ebd41000-70ebd42000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd42000-70ebd43000 r--p 00001000 103:1d 639550 /data/dalvik-cache/arm64/system@framework@boot-framework-oahl-backward-compatibility.art
+70ebd43000-70ebd44000 r--p 00005000 103:1d 639547 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.manager-V1.0-java.art
+70ebd44000-70ebd45000 r--p 00003000 103:1d 639544 /data/dalvik-cache/arm64/system@framework@boot-android.hidl.base-V1.0-java.art
+70ebd45000-70ebd46000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd46000-70ebd47000 r--p 0000f000 103:1d 639541 /data/dalvik-cache/arm64/system@framework@boot-ims-common.art
+70ebd47000-70ebd48000 r--p 0000d000 103:1d 639538 /data/dalvik-cache/arm64/system@framework@boot-voip-common.art
+70ebd48000-70ebd4a000 r--p 0005e000 103:1d 639535 /data/dalvik-cache/arm64/system@framework@boot-telephony-common.art
+70ebd4a000-70ebd4b000 r--p 00040000 103:1d 639529 /data/dalvik-cache/arm64/system@framework@boot-ext.art
+70ebd4b000-70ebd4c000 r--p 0004a000 103:1d 639526 /data/dalvik-cache/arm64/system@framework@boot-apache-xml.art
+70ebd4c000-70ebd4d000 r--p 00046000 103:1d 639523 /data/dalvik-cache/arm64/system@framework@boot-bouncycastle.art
+70ebd4d000-70ebd4e000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd4e000-70ebd53000 r--p 00225000 103:1d 639511 /data/dalvik-cache/arm64/system@framework@boot.art
+70ebd53000-70ebd5a000 rw-p 00000000 fc:00 583 /system/etc/event-log-tags
+70ebd5a000-70ebd5b000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebd5b000-70ebd5c000 r--p 0002e000 103:1d 639520 /data/dalvik-cache/arm64/system@framework@boot-okhttp.art
+70ebd5c000-70ebd5d000 r--p 00035000 103:1d 639517 /data/dalvik-cache/arm64/system@framework@boot-conscrypt.art
+70ebd5d000-70ebd5f000 r--p 000d0000 103:1d 639514 /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
+70ebd5f000-70ebd62000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebd62000-70ebd63000 rw-p 00000000 00:00 0
+70ebd63000-70ebd83000 r--s 00000000 00:10 16590 /dev/__properties__/u:object_r:exported2_default_prop:s0
+70ebd83000-70ebd84000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebd84000-70ebd89000 rw-p 00000000 00:00 0
+70ebd89000-70ebda9000 r--s 00000000 00:10 16669 /dev/__properties__/properties_serial
+70ebda9000-70ebdb3000 r--s 00000000 00:10 16560 /dev/__properties__/property_info
+70ebdb3000-70ebdb4000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdb4000-70ebdb5000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdb5000-70ebdb7000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+70ebdb7000-70ebdb8000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdb8000-70ebdba000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdba000-70ebdbb000 rw-p 00000000 00:00 0 [anon:linker_alloc]
+70ebdbb000-70ebdbd000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdbd000-70ebdbe000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebdbe000-70ebdbf000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebdbf000-70ebdc0000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc0000-70ebdc1000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdc1000-70ebdc2000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc2000-70ebdc3000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebdc3000-70ebdc5000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebdc5000-70ebde5000 r--s 00000000 00:10 16600 /dev/__properties__/u:object_r:exported_default_prop:s0
+70ebde5000-70ebde6000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebde6000-70ebde8000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebde8000-70ebe08000 r--s 00000000 00:10 16582 /dev/__properties__/u:object_r:debug_prop:s0
+70ebe08000-70ebe09000 ---p 00000000 00:00 0
+70ebe09000-70ebe0a000 rw-p 00000000 00:00 0
+70ebe0a000-70ebe0b000 ---p 00000000 00:00 0
+70ebe0b000-70ebe2b000 r--s 00000000 00:10 16669 /dev/__properties__/properties_serial
+70ebe2b000-70ebe2d000 rw-p 00000000 00:00 0 [anon:System property context nodes]
+70ebe2d000-70ebf55000 r-xp 00000000 fc:00 3184 /system/bin/linker64
+70ebf55000-70ebf5f000 r--s 00000000 00:10 16560 /dev/__properties__/property_info
+70ebf5f000-70ebf60000 r--p 00000000 00:00 0 [anon:linker_alloc]
+70ebf60000-70ebf61000 rw-p 00000000 00:00 0 [anon:linker_alloc_vector]
+70ebf61000-70ebf62000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebf62000-70ebf63000 rw-p 00000000 00:00 0 [anon:arc4random data]
+70ebf63000-70ebf64000 rw-p 00000000 00:00 0 [anon:linker_alloc_small_objects]
+70ebf64000-70ebf65000 r--p 00000000 00:00 0 [anon:atexit handlers]
+70ebf65000-70ebf66000 ---p 00000000 00:00 0 [anon:thread signal stack guard]
+70ebf66000-70ebf6a000 rw-p 00000000 00:00 0 [anon:thread signal stack]
+70ebf6a000-70ebf6b000 rw-p 00000000 00:00 0 [anon:arc4random data]
+70ebf6b000-70ebf6c000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70ebf6c000-70ebf6f000 rw-p 00000000 00:00 0 [anon:bionic TLS]
+70ebf6f000-70ebf70000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
+70ebf70000-70ebf71000 r--p 00000000 00:00 0 [vvar]
+70ebf71000-70ebf72000 r-xp 00000000 00:00 0 [vdso]
+70ebf72000-70ebf7d000 r--p 00135000 fc:00 3184 /system/bin/linker64
+70ebf7d000-70ebf7e000 rw-p 00140000 fc:00 3184 /system/bin/linker64
+70ebf7e000-70ebf81000 rw-p 00000000 00:00 0
+70ebf81000-70ebf82000 r--p 00000000 00:00 0
+70ebf82000-70ebf89000 rw-p 00000000 00:00 0
+7fc7df1000-7fc7df2000 ---p 00000000 00:00 0
+7fc7df2000-7fc85f1000 rw-p 00000000 00:00 0 [stack]
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index b894656..c7c089f 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -3,6 +3,7 @@
cc_library {
name: "libsparse",
host_supported: true,
+ recovery_available: true,
unique_host_soname: true,
srcs: [
"backed_block.c",
diff --git a/libstats/Android.bp b/libstats/Android.bp
new file mode 100644
index 0000000..d58f294
--- /dev/null
+++ b/libstats/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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.
+//
+
+// ==========================================================
+// Native library to write stats log to statsd socket
+// ==========================================================
+cc_library_static {
+ name: "libstatssocket",
+ srcs: [
+ "stats_event_list.c",
+ "statsd_writer.c",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DLIBLOG_LOG_TAG=1006",
+ "-DWRITE_TO_STATSD=1",
+ "-DWRITE_TO_LOGD=0",
+ ],
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "liblog",
+ ],
+}
diff --git a/libstats/OWNERS b/libstats/OWNERS
new file mode 100644
index 0000000..ed06fbc
--- /dev/null
+++ b/libstats/OWNERS
@@ -0,0 +1,4 @@
+bookatz@google.com
+joeo@google.com
+yaochen@google.com
+yanglu@google.com
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
new file mode 100644
index 0000000..5d174ae
--- /dev/null
+++ b/libstats/include/stats_event_list.h
@@ -0,0 +1,250 @@
+/*
+ * 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_STATS_LOG_STATS_EVENT_LIST_H
+#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+
+#include <log/log_event_list.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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+/**
+ * A copy of android_log_event_list class.
+ *
+ * android_log_event_list is going to be deprecated soon, so copy it here to
+ * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
+ * code.
+ */
+class stats_event_list {
+ private:
+ android_log_context ctx;
+ int ret;
+
+ stats_event_list(const stats_event_list&) = delete;
+ void operator=(const stats_event_list&) = delete;
+
+ public:
+ 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() {
+ int retval = android_log_destroy(&ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return retval;
+ }
+
+ /* To allow above C calls to use this class as parameter */
+ operator android_log_context() const { return ctx; }
+
+ /* return errors or transmit status */
+ int status() const { return ret; }
+
+ int begin() {
+ int retval = android_log_write_list_begin(ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+ int end() {
+ int retval = android_log_write_list_end(ctx);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ stats_event_list& operator<<(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint32_t value) {
+ int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(bool value) {
+ int retval = android_log_write_int32(ctx, value ? 1 : 0);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(uint64_t value) {
+ int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ stats_event_list& operator<<(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+#if defined(_USING_LIBCXX)
+ stats_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
+
+ stats_event_list& operator<<(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return *this;
+ }
+
+ int write(log_id_t id = LOG_ID_EVENTS) {
+ /* facilitate -EBUSY retry */
+ if ((ret == -EBUSY) || (ret > 0)) {
+ ret = 0;
+ }
+ int retval = write_to_logger(ctx, id);
+ /* existing errors trump transmission errors */
+ if (!ret) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ /*
+ * Append<Type> methods removes any integer promotion
+ * confusion, and adds access to string with length.
+ * Append methods are also added for all types for
+ * convenience.
+ */
+
+ bool AppendInt(int32_t value) {
+ int retval = android_log_write_int32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendLong(int64_t value) {
+ int retval = android_log_write_int64(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value) {
+ int retval = android_log_write_string8(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ bool AppendString(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ if (retval < 0) {
+ ret = retval;
+ }
+ 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());
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+
+ bool Append(const std::string& value) {
+ int retval = android_log_write_string8_len(ctx, value.data(), value.length());
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret;
+ }
+#endif
+
+ bool AppendFloat(float value) {
+ int retval = android_log_write_float32(ctx, value);
+ if (retval < 0) {
+ ret = retval;
+ }
+ return ret >= 0;
+ }
+
+ template <typename Tvalue>
+ bool Append(Tvalue value) {
+ *this << value;
+ return ret >= 0;
+ }
+
+ bool Append(const char* value, size_t len) {
+ int retval = android_log_write_string8_len(ctx, value, len);
+ 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 // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
new file mode 100644
index 0000000..3d746db
--- /dev/null
+++ b/libstats/stats_event_list.c
@@ -0,0 +1,174 @@
+/*
+ * 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 "include/stats_event_list.h"
+
+#include <string.h>
+#include "statsd_writer.h"
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+ uint32_t tag;
+ 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;
+ uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+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;
+
+// Similar to create_android_logger(), but instead of allocation a new buffer,
+// this function resets the buffer for resuse.
+void reset_log_context(android_log_context ctx) {
+ if (!ctx) {
+ return;
+ }
+ android_log_context_internal* context = (android_log_context_internal*)(ctx);
+ uint32_t tag = context->tag;
+ memset(context, 0, sizeof(android_log_context_internal));
+
+ context->tag = tag;
+ context->read_write_flag = kAndroidLoggerWrite;
+ size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
+ if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+ context->overflow = true;
+ }
+ /* Everything is a list */
+ context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+ context->list[0] = context->pos + 1;
+ context->pos += needed;
+}
+
+int stats_write_list(android_log_context ctx) {
+ android_log_context_internal* context;
+ const char* msg;
+ ssize_t len;
+
+ context = (android_log_context_internal*)(ctx);
+ if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+ return -EBADF;
+ }
+
+ if (context->list_nest_depth) {
+ return -EIO;
+ }
+
+ /* NB: if there was overflow, then log is truncated. Nothing reported */
+ context->storage[1] = context->count[0];
+ len = context->len = context->pos;
+ msg = (const char*)context->storage;
+ /* it's not a list */
+ if (context->count[0] <= 1) {
+ len -= sizeof(uint8_t) + sizeof(uint8_t);
+ if (len < 0) {
+ len = 0;
+ }
+ msg += sizeof(uint8_t) + sizeof(uint8_t);
+ }
+
+ struct iovec vec[2];
+ vec[0].iov_base = &context->tag;
+ vec[0].iov_len = sizeof(context->tag);
+ vec[1].iov_base = (void*)msg;
+ vec[1].iov_len = len;
+ return write_to_statsd(vec, 2);
+}
+
+int write_to_logger(android_log_context ctx, log_id_t id) {
+ int retValue = 0;
+
+ if (WRITE_TO_LOGD) {
+ retValue = android_log_write_list(ctx, id);
+ }
+
+ if (WRITE_TO_STATSD) {
+ // log_event_list's cast operator is overloaded.
+ int ret = stats_write_list(ctx);
+ // In debugging phase, we may write to both logd and statsd. Prefer to
+ // return statsd socket write error code here.
+ if (ret < 0) {
+ retValue = ret;
+ }
+ }
+
+ return retValue;
+}
+
+/* log_init_lock assumed */
+static int __write_to_statsd_initialize_locked() {
+ if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
+ if (statsdLoggerWrite.close) {
+ (*statsdLoggerWrite.close)();
+ return -ENODEV;
+ }
+ }
+ return 1;
+}
+
+static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
+ int save_errno;
+ struct timespec ts;
+ size_t len, i;
+
+ for (len = i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+ if (!len) {
+ return -EINVAL;
+ }
+
+ save_errno = errno;
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
+ errno = save_errno;
+ return ret;
+}
+
+static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
+ int ret, save_errno = errno;
+
+ statsd_writer_init_lock();
+
+ if (write_to_statsd == __write_to_statsd_init) {
+ ret = __write_to_statsd_initialize_locked();
+ if (ret < 0) {
+ statsd_writer_init_unlock();
+ errno = save_errno;
+ return ret;
+ }
+
+ write_to_statsd = __write_to_stats_daemon;
+ }
+
+ statsd_writer_init_unlock();
+
+ ret = write_to_statsd(vec, nr);
+ errno = save_errno;
+ return ret;
+}
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
new file mode 100644
index 0000000..9953bba
--- /dev/null
+++ b/libstats/statsd_writer.c
@@ -0,0 +1,260 @@
+/*
+ * 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 "statsd_writer.h"
+
+#include <cutils/sockets.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <stdarg.h>
+#include <stdatomic.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+/* branchless on many architectures. */
+#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
+
+static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void statsd_writer_init_lock() {
+ /*
+ * If we trigger a signal handler in the middle of locked activity and the
+ * signal handler logs a message, we could get into a deadlock state.
+ */
+ pthread_mutex_lock(&log_init_lock);
+}
+
+int statd_writer_trylock() {
+ return pthread_mutex_trylock(&log_init_lock);
+}
+
+void statsd_writer_init_unlock() {
+ pthread_mutex_unlock(&log_init_lock);
+}
+
+static int statsdAvailable();
+static int statsdOpen();
+static void statsdClose();
+static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
+
+struct android_log_transport_write statsdLoggerWrite = {
+ .name = "statsd",
+ .sock = -EBADF,
+ .available = statsdAvailable,
+ .open = statsdOpen,
+ .close = statsdClose,
+ .write = statsdWrite,
+};
+
+/* log_init_lock assumed */
+static int statsdOpen() {
+ int i, ret = 0;
+
+ i = atomic_load(&statsdLoggerWrite.sock);
+ if (i < 0) {
+ int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ if (sock < 0) {
+ ret = -errno;
+ } else {
+ struct sockaddr_un un;
+ memset(&un, 0, sizeof(struct sockaddr_un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, "/dev/socket/statsdw");
+
+ 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(&statsdLoggerWrite.sock, ret);
+ /* FALLTHRU */
+ default:
+ break;
+ }
+ close(sock);
+ } else {
+ ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
+ if ((ret >= 0) && (ret != sock)) {
+ close(ret);
+ }
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void __statsdClose(int negative_errno) {
+ int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
+ if (sock >= 0) {
+ close(sock);
+ }
+}
+
+static void statsdClose() {
+ __statsdClose(-EBADF);
+}
+
+static int statsdAvailable() {
+ if (atomic_load(&statsdLoggerWrite.sock) < 0) {
+ if (access("/dev/socket/statsdw", W_OK) == 0) {
+ return 0;
+ }
+ return -EBADF;
+ }
+ return 1;
+}
+
+static int statsdWrite(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 dropped;
+
+ sock = atomic_load(&statsdLoggerWrite.sock);
+ if (sock < 0) switch (sock) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ break;
+ default:
+ return -EBADF;
+ }
+ /*
+ * struct {
+ * // what we provide to socket
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ header.tid = gettid();
+ header.realtime.tv_sec = ts->tv_sec;
+ header.realtime.tv_nsec = ts->tv_nsec;
+
+ newVec[0].iov_base = (unsigned char*)&header;
+ newVec[0].iov_len = sizeof(header);
+
+ // If we dropped events before, try to tell statsd.
+ if (sock >= 0) {
+ int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+ if (snapshot) {
+ android_log_event_int_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);
+
+ newVec[headerLength].iov_base = &buffer;
+ newVec[headerLength].iov_len = sizeof(buffer);
+
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
+ if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+ atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+ }
+ }
+ }
+
+ header.id = LOG_ID_STATS;
+
+ for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
+ newVec[i].iov_base = vec[i - headerLength].iov_base;
+ payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
+
+ if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
+ newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
+ if (newVec[i].iov_len) {
+ ++i;
+ }
+ break;
+ }
+ }
+
+ /*
+ * The write below could be lost, but will never block.
+ *
+ * ENOTCONN occurs if statsd has died.
+ * ENOENT occurs if statsd is not running and socket is missing.
+ * ECONNREFUSED occurs if we can not reconnect to statsd.
+ * EAGAIN occurs if statsd is overloaded.
+ */
+ if (sock < 0) {
+ ret = sock;
+ } else {
+ ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ }
+ switch (ret) {
+ case -ENOTCONN:
+ case -ECONNREFUSED:
+ case -ENOENT:
+ if (statd_writer_trylock()) {
+ return ret; /* in a signal handler? try again when less stressed
+ */
+ }
+ __statsdClose(ret);
+ ret = statsdOpen();
+ statsd_writer_init_unlock();
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ /* FALLTHRU */
+ default:
+ break;
+ }
+
+ if (ret > (ssize_t)sizeof(header)) {
+ ret -= sizeof(header);
+ } else if (ret == -EAGAIN) {
+ atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
+ }
+
+ return ret;
+}
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
new file mode 100644
index 0000000..82e14e0
--- /dev/null
+++ b/libstats/statsd_writer.h
@@ -0,0 +1,43 @@
+/*
+ * 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_STATS_LOG_STATS_WRITER_H
+#define ANDROID_STATS_LOG_STATS_WRITER_H
+
+#include <pthread.h>
+#include <stdatomic.h>
+#include <sys/socket.h>
+
+/**
+ * Internal lock should not be exposed. This is bad design.
+ * TODO: rewrite it in c++ code and encapsulate the functionality in a
+ * StatsdWriter class.
+ */
+void statsd_writer_init_lock();
+int statsd_writer_init_trylock();
+void statsd_writer_init_unlock();
+
+struct android_log_transport_write {
+ const char* name; /* human name to describe the transport */
+ atomic_int sock;
+ int (*available)(); /* Does not cause resources to be taken */
+ int (*open)(); /* can be called multiple times, reusing current resources */
+ void (*close)(); /* free up resources */
+ /* write log to transport, returns number of bytes propagated, or -errno */
+ int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
+};
+
+#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index 82bf1bc..2e22b43 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,6 +1,7 @@
cc_library_headers {
name: "libsystem_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
export_include_dirs: ["include"],
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index 1004f06..c657526 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,8 +1,6 @@
#ifndef _SOCKET_CLIENT_H
#define _SOCKET_CLIENT_H
-#include "List.h"
-
#include <pthread.h>
#include <cutils/atomic.h>
#include <sys/types.h>
@@ -35,7 +33,7 @@
SocketClient(int sock, bool owned, bool useCmdNum);
virtual ~SocketClient();
- int getSocket() { return mSocket; }
+ int getSocket() const { return mSocket; }
pid_t getPid() const { return mPid; }
uid_t getUid() const { return mUid; }
gid_t getGid() const { return mGid; }
@@ -84,5 +82,4 @@
int sendDataLockedv(struct iovec *iov, int iovcnt);
};
-typedef android::sysutils::List<SocketClient *> SocketClientCollection;
#endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index bc93b86..56f2478 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
#include <pthread.h>
+#include <unordered_map>
+
#include <sysutils/SocketClient.h>
#include "SocketClientCommand.h"
@@ -25,7 +27,7 @@
bool mListen;
const char *mSocketName;
int mSock;
- SocketClientCollection *mClients;
+ std::unordered_map<int, SocketClient*> mClients;
pthread_mutex_t mClientsLock;
int mCtrlPipe[2];
pthread_t mThread;
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 3f8f3db..128a27a 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,13 +19,15 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/select.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
+#include <vector>
+
#include <cutils/sockets.h>
#include <log/log.h>
#include <sysutils/SocketListener.h>
@@ -52,7 +54,6 @@
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
- mClients = new SocketClientCollection();
}
SocketListener::~SocketListener() {
@@ -63,12 +64,9 @@
close(mCtrlPipe[0]);
close(mCtrlPipe[1]);
}
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end();) {
- (*it)->decRef();
- it = mClients->erase(it);
+ for (auto pair : mClients) {
+ pair.second->decRef();
}
- delete mClients;
}
int SocketListener::startListener() {
@@ -95,7 +93,7 @@
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
- mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+ mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
@@ -135,11 +133,10 @@
mSock = -1;
}
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end();) {
- delete (*it);
- it = mClients->erase(it);
+ for (auto pair : mClients) {
+ delete pair.second;
}
+ mClients.clear();
return 0;
}
@@ -152,47 +149,30 @@
}
void SocketListener::runListener() {
-
- SocketClientCollection pendingList;
-
- while(1) {
- SocketClientCollection::iterator it;
- fd_set read_fds;
- int rc = 0;
- int max = -1;
-
- FD_ZERO(&read_fds);
-
- if (mListen) {
- max = mSock;
- FD_SET(mSock, &read_fds);
- }
-
- FD_SET(mCtrlPipe[0], &read_fds);
- if (mCtrlPipe[0] > max)
- max = mCtrlPipe[0];
+ while (true) {
+ std::vector<pollfd> fds;
pthread_mutex_lock(&mClientsLock);
- for (it = mClients->begin(); it != mClients->end(); ++it) {
+ fds.reserve(2 + mClients.size());
+ fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
+ if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
+ for (auto pair : mClients) {
// NB: calling out to an other object with mClientsLock held (safe)
- int fd = (*it)->getSocket();
- FD_SET(fd, &read_fds);
- if (fd > max) {
- max = fd;
- }
+ const int fd = pair.second->getSocket();
+ if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
+ fds.push_back({.fd = fd, .events = POLLIN});
}
pthread_mutex_unlock(&mClientsLock);
- SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
- if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
- if (errno == EINTR)
- continue;
- SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+
+ SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
+ int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
+ if (rc < 0) {
+ SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
sleep(1);
continue;
- } else if (!rc)
- continue;
+ }
- if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+ if (fds[0].revents & (POLLIN | POLLERR)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) {
@@ -200,7 +180,7 @@
}
continue;
}
- if (mListen && FD_ISSET(mSock, &read_fds)) {
+ if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
@@ -208,32 +188,33 @@
continue;
}
pthread_mutex_lock(&mClientsLock);
- mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+ mClients[c] = new SocketClient(c, true, mUseCmdNum);
pthread_mutex_unlock(&mClientsLock);
}
- /* Add all active clients to the pending list first */
- pendingList.clear();
+ // Add all active clients to the pending list first, so we can release
+ // the lock before invoking the callbacks.
+ std::vector<SocketClient*> pending;
pthread_mutex_lock(&mClientsLock);
- for (it = mClients->begin(); it != mClients->end(); ++it) {
- SocketClient* c = *it;
- // NB: calling out to an other object with mClientsLock held (safe)
- int fd = c->getSocket();
- if (FD_ISSET(fd, &read_fds)) {
- pendingList.push_back(c);
+ const int size = fds.size();
+ for (int i = mListen ? 2 : 1; i < size; ++i) {
+ const struct pollfd& p = fds[i];
+ if (p.events & (POLLIN | POLLERR)) {
+ auto it = mClients.find(p.fd);
+ if (it == mClients.end()) {
+ SLOGE("fd vanished: %d", p.fd);
+ continue;
+ }
+ SocketClient* c = it->second;
+ pending.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
- /* Process the pending list, since it is owned by the thread,
- * there is no need to lock it */
- while (!pendingList.empty()) {
- /* Pop the first item from the list */
- it = pendingList.begin();
- SocketClient* c = *it;
- pendingList.erase(it);
- /* Process it, if false is returned, remove from list */
+ for (SocketClient* c : pending) {
+ // Process it, if false is returned, remove from the map
+ SLOGV("processing fd %d", c->getSocket());
if (!onDataAvailable(c)) {
release(c, false);
}
@@ -246,17 +227,10 @@
bool ret = false;
/* if our sockets are connection-based, remove and destroy it */
if (mListen && c) {
- /* Remove the client from our array */
+ /* Remove the client from our map */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end(); ++it) {
- if (*it == c) {
- mClients->erase(it);
- ret = true;
- break;
- }
- }
+ ret = (mClients.erase(c->getSocket()) != 0);
pthread_mutex_unlock(&mClientsLock);
if (ret) {
ret = c->decRef();
@@ -270,25 +244,19 @@
}
void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
- SocketClientCollection safeList;
-
- /* Add all active clients to the safe list first */
- safeList.clear();
+ // Add all clients to a separate list first, so we don't have to hold
+ // the lock while processing it.
+ std::vector<SocketClient*> clients;
pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator i;
-
- for (i = mClients->begin(); i != mClients->end(); ++i) {
- SocketClient* c = *i;
+ clients.reserve(mClients.size());
+ for (auto pair : mClients) {
+ SocketClient* c = pair.second;
c->incRef();
- safeList.push_back(c);
+ clients.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
- while (!safeList.empty()) {
- /* Pop the first item from the list */
- i = safeList.begin();
- SocketClient* c = *i;
- safeList.erase(i);
+ for (SocketClient* c : clients) {
// broadcasts are unsolicited and should not include a cmd number
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -298,25 +266,19 @@
}
void SocketListener::runOnEachSocket(SocketClientCommand *command) {
- SocketClientCollection safeList;
-
- /* Add all active clients to the safe list first */
- safeList.clear();
+ // Add all clients to a separate list first, so we don't have to hold
+ // the lock while processing it.
+ std::vector<SocketClient*> clients;
pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator i;
-
- for (i = mClients->begin(); i != mClients->end(); ++i) {
- SocketClient* c = *i;
+ clients.reserve(mClients.size());
+ for (auto pair : mClients) {
+ SocketClient* c = pair.second;
c->incRef();
- safeList.push_back(c);
+ clients.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
- while (!safeList.empty()) {
- /* Pop the first item from the list */
- i = safeList.begin();
- SocketClient* c = *i;
- safeList.erase(i);
+ for (SocketClient* c : clients) {
command->runSocketCommand(c);
c->decRef();
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index d411cee..26be64d 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -38,6 +38,7 @@
cc_library {
name: "libunwindstack",
vendor_available: true,
+ recovery_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -62,6 +63,7 @@
"MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
+ "LocalUnwinder.cpp",
"Regs.cpp",
"RegsArm.cpp",
"RegsArm64.cpp",
@@ -93,6 +95,14 @@
],
exclude_shared_libs: ["libdexfile"],
},
+ recovery: {
+ cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ exclude_srcs: [
+ "DexFile.cpp",
+ "DexFiles.cpp",
+ ],
+ exclude_shared_libs: ["libdexfile"],
+ },
},
arch: {
@@ -110,6 +120,10 @@
},
},
+ static_libs: [
+ "libprocinfo",
+ ],
+
shared_libs: [
"libbase",
"libdexfile",
@@ -121,6 +135,21 @@
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
+cc_test_library {
+ name: "libunwindstack_local",
+ defaults: ["libunwindstack_flags"],
+ srcs: ["tests/TestLocal.cpp"],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -147,6 +176,7 @@
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/JitDebugTest.cpp",
+ "tests/LocalUnwinderTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
@@ -198,6 +228,7 @@
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/offset_arm/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],
@@ -215,6 +246,15 @@
"libbase",
"liblzma",
],
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ host: {
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+ },
+ },
}
cc_binary {
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 02f8a9a..3762107 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -160,14 +160,14 @@
}
// 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, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished) {
+bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_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 + elf_offset, this, process_memory)) {
+ if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
*finished = false;
return true;
}
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 10afe33..4c05a1b 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -87,8 +87,8 @@
ISzAlloc alloc;
CXzUnpacker state;
- alloc.Alloc = [](void*, size_t size) { return malloc(size); };
- alloc.Free = [](void*, void* ptr) { return free(ptr); };
+ alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+ alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
XzUnpacker_Construct(&state, &alloc);
@@ -106,7 +106,7 @@
dst_remaining += 2 * gnu_debugdata_size_;
}
return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
- &src_remaining, CODER_FINISH_ANY, &status);
+ &src_remaining, true, CODER_FINISH_ANY, &status);
src_offset += src_remaining;
dst_offset += dst_remaining;
} while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
new file mode 100644
index 0000000..952b332
--- /dev/null
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+
+namespace unwindstack {
+
+bool LocalUnwinder::Init() {
+ pthread_rwlock_init(&maps_rwlock_, nullptr);
+
+ // Create the maps.
+ maps_.reset(new unwindstack::LocalUpdatableMaps());
+ if (!maps_->Parse()) {
+ maps_.reset();
+ return false;
+ }
+
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(getpid());
+
+ return true;
+}
+
+bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
+ for (const std::string& skip_library : skip_libraries_) {
+ if (skip_library == map_name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MapInfo* LocalUnwinder::GetMapInfo(uint64_t pc) {
+ pthread_rwlock_rdlock(&maps_rwlock_);
+ MapInfo* map_info = maps_->Find(pc);
+ pthread_rwlock_unlock(&maps_rwlock_);
+
+ if (map_info == nullptr) {
+ pthread_rwlock_wrlock(&maps_rwlock_);
+ // This is guaranteed not to invalidate any previous MapInfo objects so
+ // we don't need to worry about any MapInfo* values already in use.
+ if (maps_->Reparse()) {
+ map_info = maps_->Find(pc);
+ }
+ pthread_rwlock_unlock(&maps_rwlock_);
+ }
+
+ return map_info;
+}
+
+bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+
+ size_t num_frames = 0;
+ bool adjust_pc = false;
+ while (true) {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+
+ MapInfo* map_info = GetMapInfo(cur_pc);
+ if (map_info == nullptr) {
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(process_memory_, true);
+ uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
+ uint64_t step_pc = rel_pc;
+ uint64_t pc_adjustment;
+ if (adjust_pc) {
+ pc_adjustment = regs->GetPcAdjustment(rel_pc, elf);
+ } else {
+ pc_adjustment = 0;
+ }
+ step_pc -= pc_adjustment;
+ // Skip any locations that are within this library.
+ if (num_frames != 0 || !ShouldSkipLibrary(map_info->name)) {
+ // Add frame information.
+ std::string func_name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
+ func_name, func_offset);
+ } else {
+ frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
+ }
+ num_frames++;
+ }
+ if (!elf->valid()) {
+ break;
+ }
+ if (frame_info->size() == max_frames) {
+ 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;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index e1a1a71..e676a5a 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
#include <algorithm>
#include <cctype>
@@ -57,150 +58,16 @@
return nullptr;
}
-// Assumes that line does not end in '\n'.
-static MapInfo* InternalParseLine(const char* line) {
- // Do not use a sscanf implementation since it is not performant.
-
- // Example linux /proc/<pid>/maps lines:
- // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
- char* str;
- const char* old_str = line;
- uint64_t start = strtoull(old_str, &str, 16);
- if (old_str == str || *str++ != '-') {
- return nullptr;
- }
-
- old_str = str;
- uint64_t end = strtoull(old_str, &str, 16);
- if (old_str == str || !std::isspace(*str++)) {
- return nullptr;
- }
-
- while (std::isspace(*str)) {
- str++;
- }
-
- // Parse permissions data.
- if (*str == '\0') {
- return nullptr;
- }
- uint16_t flags = 0;
- if (*str == 'r') {
- flags |= PROT_READ;
- } else if (*str != '-') {
- return nullptr;
- }
- str++;
- if (*str == 'w') {
- flags |= PROT_WRITE;
- } else if (*str != '-') {
- return nullptr;
- }
- str++;
- if (*str == 'x') {
- flags |= PROT_EXEC;
- } else if (*str != '-') {
- return nullptr;
- }
- str++;
- if (*str != 'p' && *str != 's') {
- return nullptr;
- }
- str++;
-
- if (!std::isspace(*str++)) {
- return nullptr;
- }
-
- old_str = str;
- uint64_t offset = strtoull(old_str, &str, 16);
- if (old_str == str || !std::isspace(*str)) {
- return nullptr;
- }
-
- // Ignore the 00:00 values.
- old_str = str;
- (void)strtoull(old_str, &str, 16);
- if (old_str == str || *str++ != ':') {
- return nullptr;
- }
- if (std::isspace(*str)) {
- return nullptr;
- }
-
- // Skip the inode.
- old_str = str;
- (void)strtoull(str, &str, 16);
- if (old_str == str || !std::isspace(*str++)) {
- return nullptr;
- }
-
- // Skip decimal digit.
- old_str = str;
- (void)strtoull(old_str, &str, 10);
- if (old_str == str || (!std::isspace(*str) && *str != '\0')) {
- return nullptr;
- }
-
- while (std::isspace(*str)) {
- str++;
- }
- if (*str == '\0') {
- return new MapInfo(start, end, offset, flags, "");
- }
-
- // Save the name data.
- std::string name(str);
-
- // Mark a device map in /dev/ and not in /dev/ashmem/ specially.
- if (name.substr(0, 5) == "/dev/" && name.substr(5, 7) != "ashmem/") {
- flags |= MAPS_FLAGS_DEVICE_MAP;
- }
- return new MapInfo(start, end, offset, flags, name);
-}
-
bool Maps::Parse() {
- int fd = open(GetMapsFile().c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- return false;
- }
-
- bool return_value = true;
- char buffer[2048];
- size_t leftover = 0;
- while (true) {
- ssize_t bytes = read(fd, &buffer[leftover], 2048 - leftover);
- if (bytes == -1) {
- return_value = false;
- break;
- }
- if (bytes == 0) {
- break;
- }
- bytes += leftover;
- char* line = buffer;
- while (bytes > 0) {
- char* newline = static_cast<char*>(memchr(line, '\n', bytes));
- if (newline == nullptr) {
- memmove(buffer, line, bytes);
- break;
- }
- *newline = '\0';
-
- MapInfo* map_info = InternalParseLine(line);
- if (map_info == nullptr) {
- return_value = false;
- break;
- }
- maps_.push_back(map_info);
-
- bytes -= newline - line + 1;
- line = newline + 1;
- }
- leftover = bytes;
- }
- close(fd);
- return return_value;
+ return android::procinfo::ReadMapFile(
+ GetMapsFile(),
+ [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, 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(start, end, pgoff, flags, name));
+ });
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
@@ -222,30 +89,99 @@
}
bool BufferMaps::Parse() {
- const char* start_of_line = buffer_;
- do {
- std::string line;
- const char* end_of_line = strchr(start_of_line, '\n');
- if (end_of_line == nullptr) {
- line = start_of_line;
- } else {
- line = std::string(start_of_line, end_of_line - start_of_line);
- end_of_line++;
- }
-
- MapInfo* map_info = InternalParseLine(line.c_str());
- if (map_info == nullptr) {
- return false;
- }
- maps_.push_back(map_info);
-
- start_of_line = end_of_line;
- } while (start_of_line != nullptr && *start_of_line != '\0');
- return true;
+ 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) {
+ // 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(start, end, pgoff, flags, name));
+ });
}
const std::string RemoteMaps::GetMapsFile() const {
return "/proc/" + std::to_string(pid_) + "/maps";
}
+const std::string LocalUpdatableMaps::GetMapsFile() const {
+ return "/proc/self/maps";
+}
+
+bool LocalUpdatableMaps::Reparse() {
+ // 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;
+ }
+
+ 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];
+ 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];
+ 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;
+ } else if (info->start > start) {
+ // Stop, there isn't going to be a match.
+ search_map_idx = old_map_idx;
+ break;
+ }
+
+ // 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);
+ maps_[old_map_idx] = nullptr;
+ total_entries--;
+ }
+ if (search_map_idx >= last_map_idx) {
+ break;
+ }
+ }
+
+ // 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]);
+ 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) {
+ if (a == nullptr) {
+ return false;
+ } else if (b == nullptr) {
+ return true;
+ }
+ return a->start < b->start;
+ });
+ maps_.resize(total_entries);
+
+ return true;
+}
+
+LocalUpdatableMaps::~LocalUpdatableMaps() {
+ for (auto map_info : saved_maps_) {
+ delete map_info;
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 9a6c6df..099cc9e 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -220,8 +220,7 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
- &finished);
+ stepped = elf->Step(rel_pc, step_pc, regs_, process_memory_.get(), &finished);
elf->GetLastError(&last_error_);
if (stepped && finished) {
break;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 385973e..f4cdbda 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -65,8 +65,8 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
- Memory* process_memory, bool* finished);
+ bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, Regs* regs, Memory* process_memory,
+ bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
new file mode 100644
index 0000000..80bb53e
--- /dev/null
+++ b/libunwindstack/include/unwindstack/LocalUnwinder.h
@@ -0,0 +1,86 @@
+/*
+ * 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 _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+struct MapInfo;
+
+struct LocalFrameData {
+ LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
+ uint64_t function_offset)
+ : map_info(map_info),
+ pc(pc),
+ rel_pc(rel_pc),
+ function_name(function_name),
+ function_offset(function_offset) {}
+
+ MapInfo* map_info;
+ uint64_t pc;
+ uint64_t rel_pc;
+ std::string function_name;
+ uint64_t function_offset;
+};
+
+// This is a specialized class that should only be used for doing local unwinds.
+// The Unwind call can be made as multiple times on the same object, and it can
+// be called by multiple threads at the same time.
+// It is designed to be used in debugging circumstances to get a stack trace
+// as fast as possible.
+class LocalUnwinder {
+ public:
+ LocalUnwinder() = default;
+ LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
+ ~LocalUnwinder() = default;
+
+ bool Init();
+
+ bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
+
+ bool ShouldSkipLibrary(const std::string& map_name);
+
+ MapInfo* GetMapInfo(uint64_t pc);
+
+ ErrorCode LastErrorCode() { return last_error_.code; }
+ uint64_t LastErrorAddress() { return last_error_.address; }
+
+ private:
+ pthread_rwlock_t maps_rwlock_;
+ std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
+ std::shared_ptr<Memory> process_memory_;
+ std::vector<std::string> skip_libraries_;
+ ErrorData last_error_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index a57fe68..ac0df41 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -34,6 +34,13 @@
struct MapInfo {
MapInfo() = default;
MapInfo(uint64_t start, uint64_t end) : start(start), end(end) {}
+ MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name)
+ : start(start),
+ end(end),
+ offset(offset),
+ flags(flags),
+ name(name),
+ load_bias(static_cast<uint64_t>(-1)) {}
MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name)
: start(start),
end(end),
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 74e5c47..67fbed2 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -87,6 +87,19 @@
virtual ~LocalMaps() = default;
};
+class LocalUpdatableMaps : public Maps {
+ public:
+ LocalUpdatableMaps() : Maps() {}
+ virtual ~LocalUpdatableMaps();
+
+ bool Reparse();
+
+ const std::string GetMapsFile() const override;
+
+ private:
+ std::vector<MapInfo*> saved_maps_;
+};
+
class BufferMaps : public Maps {
public:
BufferMaps(const char* buffer) : buffer_(buffer) {}
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f9028c4..aecbf6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -133,7 +133,7 @@
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
bool finished;
- ASSERT_FALSE(elf.Step(0, 0, 0, nullptr, nullptr, &finished));
+ ASSERT_FALSE(elf.Step(0, 0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
@@ -330,7 +330,7 @@
elf.FakeSetValid(true);
elf.FakeSetLoadBias(0);
bool finished;
- ASSERT_TRUE(elf.Step(0x1000, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x3000, 0x1000, ®s, &process_memory, &finished));
EXPECT_FALSE(finished);
EXPECT_EQ(15U, regs.pc());
EXPECT_EQ(13U, regs.sp());
@@ -370,7 +370,7 @@
EXPECT_CALL(*interface, Step(0x1000, 0, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1004, 0x1000, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
@@ -388,7 +388,7 @@
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x7304, 0x7300, ®s, &process_memory, &finished));
}
TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
new file mode 100644
index 0000000..56a18cd
--- /dev/null
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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 <dlfcn.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/LocalUnwinder.h>
+
+namespace unwindstack {
+
+static std::vector<LocalFrameData>* g_frame_info;
+static LocalUnwinder* g_unwinder;
+
+extern "C" void SignalLocalInnerFunction() {
+ g_unwinder->Unwind(g_frame_info, 256);
+}
+
+extern "C" void SignalLocalMiddleFunction() {
+ SignalLocalInnerFunction();
+}
+
+extern "C" void SignalLocalOuterFunction() {
+ SignalLocalMiddleFunction();
+}
+
+static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
+ SignalLocalOuterFunction();
+}
+
+static std::string ErrorMsg(const std::vector<const char*>& function_names,
+ const std::vector<LocalFrameData>& frame_info) {
+ std::string unwind;
+ size_t i = 0;
+ for (const auto& frame : frame_info) {
+ unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
+ frame.pc, frame.rel_pc);
+ if (frame.map_info != nullptr) {
+ if (!frame.map_info->name.empty()) {
+ unwind += " " + frame.map_info->name;
+ } else {
+ unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start,
+ frame.map_info->end);
+ }
+ if (frame.map_info->offset != 0) {
+ unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset);
+ }
+ }
+ if (!frame.function_name.empty()) {
+ unwind += " " + frame.function_name;
+ if (frame.function_offset != 0) {
+ unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ }
+ unwind += '\n';
+ }
+
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
+}
+
+// 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 LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ std::vector<LocalFrameData> frame_info;
+ g_frame_info = &frame_info;
+ g_unwinder = unwinder;
+ std::vector<const char*> expected_function_names;
+
+ if (unwind_through_signal) {
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = SignalLocalCallerHandler;
+ act.sa_flags = SA_RESTART | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+
+ raise(SIGUSR1);
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
+ "LocalInnerFunction", "SignalLocalOuterFunction",
+ "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
+ } else {
+ ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
+
+ expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
+ }
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalInnerFunction(unwinder, unwind_through_signal);
+}
+
+extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
+ LocalMiddleFunction(unwinder, unwind_through_signal);
+}
+
+class LocalUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ unwinder_.reset(new LocalUnwinder);
+ ASSERT_TRUE(unwinder_->Init());
+ }
+
+ std::unique_ptr<LocalUnwinder> unwinder_;
+};
+
+TEST_F(LocalUnwinderTest, local) {
+ LocalOuterFunction(unwinder_.get(), false);
+}
+
+TEST_F(LocalUnwinderTest, local_signal) {
+ LocalOuterFunction(unwinder_.get(), true);
+}
+
+TEST_F(LocalUnwinderTest, local_multiple) {
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
+}
+
+// This test verifies that doing an unwind before and after a dlopen
+// works. It's verifying that the maps read during the first unwind
+// do not cause a problem when doing the unwind using the code in
+// the dlopen'd code.
+TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
+ // Prime the maps data.
+ ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
+
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value == std::string::npos) {
+ testlib = "../";
+ } else {
+ testlib = testlib.substr(0, value + 1) + "../";
+ }
+ testlib += "libunwindstack_local.so";
+
+ void* handle = dlopen(testlib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr);
+
+ void (*unwind_function)(void*, void*) =
+ reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
+ ASSERT_TRUE(unwind_function != nullptr);
+
+ std::vector<LocalFrameData> frame_info;
+ unwind_function(unwinder_.get(), &frame_info);
+
+ ASSERT_EQ(0, dlclose(handle));
+
+ std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
+ "TestlibLevel3", "TestlibLevel4"};
+
+ for (auto& frame : frame_info) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
+ if (expected_function_names.empty()) {
+ break;
+ }
+ }
+ }
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
new file mode 100644
index 0000000..fa0baff
--- /dev/null
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <unwindstack/LocalUnwinder.h>
+
+#include <vector>
+
+extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
+ unwindstack::LocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
+ std::vector<unwindstack::LocalFrameData>* frame_info =
+ reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
+ unwinder->Unwind(frame_info, 256);
+}
+
+extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
+ TestlibLevel4(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
+ TestlibLevel3(unwinder_data, frame_data);
+}
+
+extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
+ TestlibLevel2(unwinder_data, frame_data);
+}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 2b8f0c2..285fc9e 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -46,6 +46,12 @@
namespace unwindstack {
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
class UnwindOfflineTest : public ::testing::Test {
protected:
void TearDown() override {
@@ -64,9 +70,24 @@
maps_.reset(new BufferMaps(data.c_str()));
ASSERT_TRUE(maps_->Parse());
- 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());
+ 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());
+ }
+ process_memory_.reset(stack_memory.release());
+ }
switch (arch) {
case ARCH_ARM: {
@@ -180,7 +201,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
- Init("straddle_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -204,7 +225,7 @@
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
- Init("gnu_debugdata_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -224,7 +245,7 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
- Init("straddle_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -255,14 +276,8 @@
EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
}
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
- MemoryOffline* memory = new MemoryOffline;
- ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
- parts->Add(memory);
-}
-
TEST_F(UnwindOfflineTest, jit_debug_x86) {
- Init("jit_debug_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -555,7 +570,7 @@
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
- Init("jit_debug_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -873,7 +888,7 @@
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
- Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64);
+ ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -902,7 +917,7 @@
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
- Init("debug_frame_first_x86/", ARCH_X86);
+ ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -930,7 +945,7 @@
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
- Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64);
+ ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -957,7 +972,7 @@
}
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
- Init("art_quick_osr_stub_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
MemoryOfflineParts* memory = new MemoryOfflineParts;
AddMemory(dir_ + "descriptor.data", memory);
@@ -1072,7 +1087,7 @@
}
TEST_F(UnwindOfflineTest, jit_map_arm) {
- Init("jit_map_arm/", ARCH_ARM);
+ ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
"jit_map0.so", 0);
@@ -1111,4 +1126,78 @@
EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
}
+TEST_F(UnwindOfflineTest, offset_arm) {
+ ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ 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"
+ " #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",
+ frame_info);
+
+ EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xf43d2ce0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x2e55ff3U, unwinder.frames()[2].pc);
+ 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(0xf43d2d10U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x2e562d9U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffcc0ee0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0x2e56c4fU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffcc1060U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0x2e56c81U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffcc1078U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0x2e58547U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffcc1090U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0x2e58d99U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffcc1438U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0x2e7e453U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffcc1448U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x2e7ede7U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffcc1458U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0x2e7f105U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffcc1490U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0x2e84215U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffcc14c0U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x2e83f4fU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffcc1510U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x2e773dbU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffcc1528U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf41a2c0dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffcc1540U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0x2b6c02fU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffcc1558U, unwinder.frames()[18].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 242cc6a..83695bb 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -106,15 +106,12 @@
Unwinder unwinder(512, maps, regs, process_memory);
unwinder.Unwind();
- std::string expected_function = expected_function_names.back();
- expected_function_names.pop_back();
for (auto& frame : unwinder.frames()) {
- if (frame.function_name == expected_function) {
+ if (frame.function_name == expected_function_names.back()) {
+ expected_function_names.pop_back();
if (expected_function_names.empty()) {
break;
}
- expected_function = expected_function_names.back();
- expected_function_names.pop_back();
}
}
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
new file mode 100644
index 0000000..9f5c8ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
new file mode 100644
index 0000000..7a30bfa
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/tests/files/offline/offset_arm/maps.txt
new file mode 100644
index 0000000..6224464
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/maps.txt
@@ -0,0 +1,2 @@
+2b6c000-2e92000 r-xp 42000 00:00 0 libunwindstack_test
+f4135000-f41a9000 r-xp 25000 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/tests/files/offline/offset_arm/regs.txt
new file mode 100644
index 0000000..1f4ac8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: 5
+r1: 5
+r2: 4
+r3: 1
+r4: 73804b6b
+r5: f3c9c000
+r6: 2ea09ac
+r7: 10624dd3
+r8: f41b5d8c
+r9: f3c9c000
+r10: 6f17
+r11: f3c94048
+ip: 2ea0807
+sp: f43d2ccc
+lr: 2e55fef
+pc: 2e55fa0
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/tests/files/offline/offset_arm/stack0.data
new file mode 100644
index 0000000..23a9874
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/tests/files/offline/offset_arm/stack1.data
new file mode 100644
index 0000000..49bdd1e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 74868d4..640992f 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -30,6 +30,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include <unwindstack/Elf.h>
@@ -68,7 +69,7 @@
bool SaveRegs(unwindstack::Regs* regs) {
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("regs.txt", "w+"), &fclose);
if (fp == nullptr) {
- printf("Failed to create file regs.txt.\n");
+ perror("Failed to create file regs.txt");
return false;
}
regs->IterateRegisters([&fp](const char* name, uint64_t value) {
@@ -78,30 +79,45 @@
return true;
}
-bool SaveStack(pid_t pid, uint64_t sp_start, uint64_t sp_end) {
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("stack.data", "w+"), &fclose);
- if (fp == nullptr) {
- printf("Failed to create stack.data.\n");
- return false;
- }
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+ for (size_t i = 0; i < stacks.size(); i++) {
+ std::string file_name;
+ if (stacks.size() != 1) {
+ file_name = "stack" + std::to_string(i) + ".data";
+ } else {
+ file_name = "stack.data";
+ }
- size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
- if (bytes != sizeof(sp_start)) {
- perror("Failed to write all data.");
- return false;
- }
+ // Do this first, so if it fails, we don't create the file.
+ uint64_t sp_start = stacks[i].first;
+ uint64_t sp_end = stacks[i].second;
+ std::vector<uint8_t> buffer(sp_end - sp_start);
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
+ printf("Unable to read stack data.\n");
+ return false;
+ }
- std::vector<uint8_t> buffer(sp_end - sp_start);
- auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
- printf("Unable to read stack data.\n");
- return false;
- }
+ printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
- bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
- if (bytes != buffer.size()) {
- printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
- return 1;
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
+ if (fp == nullptr) {
+ perror("Failed to create stack.data");
+ return false;
+ }
+
+ size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
+ if (bytes != sizeof(sp_start)) {
+ printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
+ bytes);
+ return false;
+ }
+
+ bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
+ if (bytes != buffer.size()) {
+ printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+ return false;
+ }
}
return true;
@@ -110,17 +126,11 @@
bool CreateElfFromMemory(std::shared_ptr<unwindstack::Memory>& memory, map_info_t* info) {
std::string cur_name;
if (info->name.empty()) {
- cur_name = android::base::StringPrintf("anonymous:%" PRIx64, info->start);
+ cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
} else {
- cur_name = basename(info->name.c_str());
- cur_name = android::base::StringPrintf("%s:%" PRIx64, basename(info->name.c_str()), info->start);
+ cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
}
- std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
- if (output == nullptr) {
- printf("Cannot create %s\n", cur_name.c_str());
- return false;
- }
std::vector<uint8_t> buffer(info->end - info->start);
// If this is a mapped in file, it might not be possible to read the entire
// map, so read all that is readable.
@@ -129,6 +139,13 @@
printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
return false;
}
+
+ std::unique_ptr<FILE, decltype(&fclose)> output(fopen(cur_name.c_str(), "w+"), &fclose);
+ if (output == nullptr) {
+ perror((std::string("Cannot create ") + cur_name).c_str());
+ return false;
+ }
+
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
@@ -144,13 +161,14 @@
bool CopyElfFromFile(map_info_t* info) {
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) {
- printf("Cannot create file %s\n", cur_name.c_str());
+ perror((std::string("Cannot create file " + cur_name)).c_str());
return false;
}
std::vector<uint8_t> buffer(10000);
@@ -198,9 +216,21 @@
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
- uint64_t last_sp;
+ std::vector<std::pair<uint64_t, uint64_t>> stacks;
+ uint64_t sp_map_start = 0;
+ 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()) {
- last_sp = frame.sp;
+ 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;
@@ -211,7 +241,12 @@
// 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 1;
+ printf("Ignoring map ");
+ if (!info->name.empty()) {
+ printf("%s\n", info->name.c_str());
+ } else {
+ printf("anonymous:%" PRIx64 "\n", info->start);
+ }
}
}
}
@@ -221,7 +256,7 @@
printf("%s\n", unwinder.FormatFrame(i).c_str());
}
- if (!SaveStack(pid, sp, last_sp)) {
+ if (!SaveStack(pid, stacks)) {
return 1;
}
@@ -232,7 +267,7 @@
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
if (fp == nullptr) {
- printf("Failed to create maps.txt.\n");
+ perror("Failed to create maps.txt");
return false;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 0d7925a..9395ef8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -15,6 +15,7 @@
cc_library_headers {
name: "libutils_headers",
vendor_available: true,
+ recovery_available: true,
host_supported: true,
header_libs: [
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 80711bc..1cfef34 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -472,47 +472,50 @@
return;
}
- if (params.oomadj >= 900) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 800) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 700) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 600) {
- // Launcher should be perceptible, don't kill it.
- params.oomadj = 200;
- soft_limit_mult = 1;
- } else if (params.oomadj >= 500) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 400) {
- soft_limit_mult = 0;
- } else if (params.oomadj >= 300) {
- soft_limit_mult = 1;
- } else if (params.oomadj >= 200) {
- soft_limit_mult = 2;
- } else if (params.oomadj >= 100) {
- soft_limit_mult = 10;
- } else if (params.oomadj >= 0) {
- soft_limit_mult = 20;
- } else {
- // Persistent processes will have a large
- // soft limit 512MB.
- soft_limit_mult = 64;
+ if (low_ram_device) {
+ if (params.oomadj >= 900) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 800) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 700) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 600) {
+ // Launcher should be perceptible, don't kill it.
+ params.oomadj = 200;
+ soft_limit_mult = 1;
+ } else if (params.oomadj >= 500) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 400) {
+ soft_limit_mult = 0;
+ } else if (params.oomadj >= 300) {
+ soft_limit_mult = 1;
+ } else if (params.oomadj >= 200) {
+ soft_limit_mult = 2;
+ } else if (params.oomadj >= 100) {
+ soft_limit_mult = 10;
+ } else if (params.oomadj >= 0) {
+ soft_limit_mult = 20;
+ } else {
+ // Persistent processes will have a large
+ // soft limit 512MB.
+ soft_limit_mult = 64;
+ }
+
+ snprintf(path, sizeof(path), MEMCG_SYSFS_PATH
+ "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+ params.uid, params.pid);
+ snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
+
+ /*
+ * system_server process has no memcg under /dev/memcg/apps but should be
+ * registered with lmkd. This is the best way so far to identify it.
+ */
+ is_system_server = (params.oomadj == SYSTEM_ADJ &&
+ (pwdrec = getpwnam("system")) != NULL &&
+ params.uid == pwdrec->pw_uid);
+ writefilestring(path, val, !is_system_server);
}
- snprintf(path, sizeof(path), MEMCG_SYSFS_PATH "apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
- params.uid, params.pid);
- snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
-
- /*
- * system_server process has no memcg under /dev/memcg/apps but should be
- * registered with lmkd. This is the best way so far to identify it.
- */
- is_system_server = (params.oomadj == SYSTEM_ADJ &&
- (pwdrec = getpwnam("system")) != NULL &&
- params.uid == pwdrec->pw_uid);
- writefilestring(path, val, !is_system_server);
-
procp = pid_lookup(params.pid);
if (!procp) {
procp = malloc(sizeof(struct proc));
@@ -1150,8 +1153,15 @@
}
}
- if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
+ if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
+ if (debug_process_killing) {
+ ALOGI("Ignore %s memory pressure event "
+ "(free memory=%ldkB, cache=%ldkB, limit=%ldkB)",
+ level_name[level], other_free * page_k, other_file * page_k,
+ (long)lowmem_minfree[lowmem_targets_size - 1] * page_k);
+ }
return;
+ }
/* Free up enough pages to push over the highest minfree level */
pages_to_free = lowmem_minfree[lowmem_targets_size - 1] -
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 7c40a77..750761f 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -67,8 +67,9 @@
# ZygoteInit class preloading ends:
3030 boot_progress_preload_end (time|2|3)
-# Dalvik VM
+# Dalvik VM / ART
20003 dvm_lock_sample (process|3),(main|1|5),(thread|3),(time|1|3),(file|3),(line|1|5),(ownerfile|3),(ownerline|1|5),(sample_percent|1|6)
+20004 art_hidden_api_access (access_method|1),(flags|1),(class|3),(member|3),(type_signature|3)
75000 sqlite_mem_alarm_current (current|1|2)
75001 sqlite_mem_alarm_max (max|1|2)
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 269db2f..27cd9a8 100755
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -112,7 +112,7 @@
std::map<std::string, std::string> LogAudit::populateDenialMap() {
std::ifstream bug_file("/system/etc/selinux/selinux_denial_metadata");
std::string line;
- // allocate a map for the static map pointer in logParse to keep track of,
+ // allocate a map for the static map pointer in auditParse to keep track of,
// this function only runs once
std::map<std::string, std::string> denial_to_bug;
if (bug_file.good()) {
@@ -140,7 +140,8 @@
return "";
}
-void LogAudit::logParse(const std::string& string, std::string* bug_num) {
+void LogAudit::auditParse(const std::string& string, uid_t uid,
+ std::string* bug_num) {
if (!__android_log_is_debuggable()) {
bug_num->assign("");
return;
@@ -162,6 +163,11 @@
} else {
bug_num->assign("");
}
+
+ if (uid >= AID_APP_START && uid <= AID_APP_END) {
+ bug_num->append(" app=");
+ bug_num->append(android::uidToName(uid));
+ }
}
int LogAudit::logPrint(const char* fmt, ...) {
@@ -190,8 +196,27 @@
while ((cp = strstr(str, " "))) {
memmove(cp, cp + 1, strlen(cp + 1) + 1);
}
+ pid_t pid = getpid();
+ pid_t tid = gettid();
+ uid_t uid = AID_LOGD;
+ static const char pid_str[] = " pid=";
+ char* pidptr = strstr(str, pid_str);
+ if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
+ cp = pidptr + sizeof(pid_str) - 1;
+ pid = 0;
+ while (isdigit(*cp)) {
+ pid = (pid * 10) + (*cp - '0');
+ ++cp;
+ }
+ tid = pid;
+ logbuf->wrlock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ memmove(pidptr, cp, strlen(cp) + 1);
+ }
+
bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
- static std::string bug_metadata;
+ static std::string denial_metadata;
if ((fdDmesg >= 0) && initialized) {
struct iovec iov[4];
static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
@@ -228,8 +253,8 @@
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*>(bug_metadata.c_str());
- iov[2].iov_len = bug_metadata.length();
+ 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);
@@ -249,14 +274,14 @@
last_info = info;
}
if (count == 0) {
- logParse(str, &bug_metadata);
+ 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*>(bug_metadata.c_str());
- iov[2].iov_len = bug_metadata.length();
+ 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);
@@ -269,9 +294,6 @@
return 0;
}
- pid_t pid = getpid();
- pid_t tid = gettid();
- uid_t uid = AID_LOGD;
log_time now;
static const char audit_str[] = " audit(";
@@ -296,29 +318,13 @@
now = log_time(CLOCK_REALTIME);
}
- static const char pid_str[] = " pid=";
- char* pidptr = strstr(str, pid_str);
- if (pidptr && isdigit(pidptr[sizeof(pid_str) - 1])) {
- cp = pidptr + sizeof(pid_str) - 1;
- pid = 0;
- while (isdigit(*cp)) {
- pid = (pid * 10) + (*cp - '0');
- ++cp;
- }
- tid = pid;
- logbuf->wrlock();
- uid = logbuf->pidToUid(pid);
- logbuf->unlock();
- memmove(pidptr, cp, strlen(cp) + 1);
- }
-
// log to events
size_t str_len = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD);
if (((fdDmesg < 0) || !initialized) && !hasMetadata(str, str_len))
- logParse(str, &bug_metadata);
- str_len = (str_len + bug_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
- ? str_len + bug_metadata.length()
+ auditParse(str, uid, &denial_metadata);
+ str_len = (str_len + denial_metadata.length() <= LOGGER_ENTRY_MAX_PAYLOAD)
+ ? str_len + denial_metadata.length()
: LOGGER_ENTRY_MAX_PAYLOAD;
size_t message_len = str_len + sizeof(android_log_event_string_t);
@@ -332,9 +338,9 @@
event->header.tag = htole32(AUDITD_LOG_TAG);
event->type = EVENT_TYPE_STRING;
event->length = htole32(str_len);
- memcpy(event->data, str, str_len - bug_metadata.length());
- memcpy(event->data + str_len - bug_metadata.length(),
- bug_metadata.c_str(), bug_metadata.length());
+ memcpy(event->data, str, str_len - denial_metadata.length());
+ memcpy(event->data + str_len - denial_metadata.length(),
+ denial_metadata.c_str(), denial_metadata.length());
rc = logbuf->log(
LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char*>(event),
@@ -380,7 +386,8 @@
prefix_len = LOGGER_ENTRY_MAX_PAYLOAD;
}
size_t suffix_len = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - prefix_len);
- message_len = str_len + prefix_len + suffix_len + bug_metadata.length() + 2;
+ message_len =
+ str_len + prefix_len + suffix_len + denial_metadata.length() + 2;
if (main) { // begin scope for main buffer
char newstr[message_len];
@@ -390,7 +397,7 @@
strncpy(newstr + 1 + str_len, str, prefix_len);
strncpy(newstr + 1 + str_len + prefix_len, ecomm, suffix_len);
strncpy(newstr + 1 + str_len + prefix_len + suffix_len,
- bug_metadata.c_str(), bug_metadata.length());
+ denial_metadata.c_str(), denial_metadata.length());
rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr,
(message_len <= USHRT_MAX) ? (unsigned short)message_len
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 5904966..c3d7a3e 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -48,7 +48,7 @@
std::map<std::string, std::string> populateDenialMap();
std::string denialParse(const std::string& denial, char terminator,
const std::string& search_term);
- void logParse(const std::string& string, std::string* bug_num);
+ void auditParse(const std::string& string, uid_t uid, std::string* bug_num);
int logPrint(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)));
};
diff --git a/logd/main.cpp b/logd/main.cpp
index 4af0d21..606aa63 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,7 +33,6 @@
#include <syslog.h>
#include <unistd.h>
-#include <cstdbool>
#include <memory>
#include <android-base/macros.h>
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index d4ba4f4..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -12,6 +12,7 @@
cc_library {
name: "liblogwrap",
defaults: ["logwrapper_defaults"],
+ recovery_available: true,
srcs: ["logwrap.c"],
shared_libs: [
"libcutils",
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index 406e208..bce308b 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -111,7 +111,7 @@
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
- uint64_t recovery_dtbo_offset; /* physical load addr */
+ uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
uint32_t header_size;
} __attribute__((packed));
@@ -138,11 +138,14 @@
* 1. kernel and ramdisk are required (size != 0)
* 2. recovery_dtbo 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, recovery_dtbo) at
+ * 4. load each element (kernel, ramdisk, second) at
* the specified physical address (kernel_addr, etc)
- * 5. prepare tags at tag_addr. kernel_args[] is
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo 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.
- * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- * 7. if second_size != 0: jump to second_addr
+ * 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/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ea9b968..70f6faa 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -2,6 +2,7 @@
name: "libpropertyinfoparser",
host_supported: true,
vendor_available: true,
+ recovery_available: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index 93c347b..c6bda4a 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -3,6 +3,7 @@
cc_library_static {
name: "libqemu_pipe",
vendor_available: true,
+ recovery_available: true,
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f488ed5..3c9e5f3 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -147,13 +147,10 @@
bcp_md5 :=
bcp_dep :=
-# If BOARD_VNDK_VERSION is defined, append PLATFORM_VNDK_VERSION to base name.
+# Append PLATFORM_VNDK_VERSION to base name.
define append_vndk_version
$(strip \
- $(if $(BOARD_VNDK_VERSION), \
- $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)), \
- $(1) \
- ) \
+ $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)) \
)
endef
@@ -215,31 +212,46 @@
vndk_version_suffix :=
endef # update_and_install_ld_config
+
+#######################################
+# ld.config.txt selection variables
+#
+_enforce_vndk_at_runtime := false
+ifdef BOARD_VNDK_VERSION
+ ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
+ _enforce_vndk_at_runtime := true
+ endif
+endif
+
+_enforce_vndk_lite_at_runtime := false
+ifeq ($(_enforce_vndk_at_runtime),false)
+ ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
+ _enforce_vndk_lite_at_runtime := true
+ endif
+endif
+
#######################################
# ld.config.txt
#
# For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
# "ld.config.txt" as a source file. This configuration includes strict VNDK
# run-time restrictions for vendor process.
+#
# Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.vndk_lite.txt"
# as a source file. This configuration does not have strict VNDK run-time
# restrictions.
+#
# If the device is not treblized, use "ld.config.legacy.txt" for legacy
# namespace configuration.
+#
include $(CLEAR_VARS)
LOCAL_MODULE := ld.config.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-_enforce_vndk_at_runtime := false
-ifdef BOARD_VNDK_VERSION
-ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
- _enforce_vndk_at_runtime := true
-endif
-endif
-
ifeq ($(_enforce_vndk_at_runtime),true)
+
# for VNDK enforced devices
LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
@@ -248,37 +260,36 @@
$(LOCAL_BUILT_MODULE),\
$(PLATFORM_VNDK_VERSION)))
-else ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-# for treblized but VNDK non-enforced devices
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+else ifeq ($(_enforce_vndk_lite_at_runtime),true)
+
+# for treblized but VNDK lightly enforced devices
+LOCAL_MODULE_STEM := ld.config.vndk_lite.txt
include $(BUILD_SYSTEM)/base_rules.mk
$(eval $(call update_and_install_ld_config,\
$(LOCAL_PATH)/etc/ld.config.vndk_lite.txt,\
$(LOCAL_BUILT_MODULE),\
- $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION)),\
+ $(PLATFORM_VNDK_VERSION),\
true))
else
+
# for legacy non-treblized devices
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_SRC_FILES := etc/ld.config.legacy.txt
include $(BUILD_PREBUILT)
-endif # if _enforce_vndk_at_runtime is true
+endif # ifeq ($(_enforce_vndk_at_runtime),true)
-_enforce_vndk_at_runtime :=
#######################################
-# ld.config.noenforce.txt
+# ld.config.vndk_lite.txt
#
-# This file is a temporary configuration file only for GSI. Originally GSI has
-# BOARD_VNDK_VERSION defined and has strict VNDK enforcing rule based on
-# "ld.config.txt". However for the devices, that have not defined
-# BOARD_VNDK_VERSION, GSI provides this configuration file which is based on
-# "ld.config.vndk_lite.txt".
-# Do not install this file for the devices other than GSI.
+# This module is only for GSI.
+#
+ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
include $(CLEAR_VARS)
-LOCAL_MODULE := ld.config.noenforce.txt
+LOCAL_MODULE := ld.config.vndk_lite.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
@@ -289,6 +300,11 @@
$(PLATFORM_VNDK_VERSION),\
true))
+endif # ifeq ($(_enforce_vndk_lite_at_runtime),false)
+
+_enforce_vndk_at_runtime :=
+_enforce_vndk_lite_at_runtime :=
+
#######################################
# llndk.libraries.txt
include $(CLEAR_VARS)
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index ba96cc8..a0b1996 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 = /product/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -37,7 +38,8 @@
###############################################################################
namespace.default.isolated = true
-namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /product/${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
@@ -49,6 +51,7 @@
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 += /product/${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
@@ -68,6 +71,8 @@
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 += /product/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -83,6 +88,7 @@
namespace.default.asan.permitted.paths += /odm/app
namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /product/${LIB}
namespace.default.asan.permitted.paths += /product/framework
namespace.default.asan.permitted.paths += /product/app
namespace.default.asan.permitted.paths += /product/priv-app
@@ -320,10 +326,13 @@
###############################################################################
namespace.system.isolated = false
-namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /product/${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 += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -335,4 +344,5 @@
###############################################################################
[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.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 1fd4195..db65c14 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 = /product/bin/
dir.vendor = /odm/bin/
dir.vendor = /vendor/bin/
@@ -40,6 +41,7 @@
namespace.default.search.paths = /system/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -47,6 +49,8 @@
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}
###############################################################################
# "sphal" namespace
@@ -205,6 +209,7 @@
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /product/${LIB}
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
@@ -224,6 +229,8 @@
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 += /product/${LIB}
###############################################################################
# Namespace config for binaries under /postinstall.
@@ -235,4 +242,5 @@
###############################################################################
[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/init.rc b/rootdir/init.rc
index d3504ad..197047d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -419,6 +419,7 @@
mkdir /data/misc/radio 0770 system radio
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
+ mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn